<!DOCTYPE html>



  


<html class="theme-next gemini use-motion" lang="zh-Hans">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta name="theme-color" content="#222">






  
  
  <link rel="stylesheet" media="all" href="/lib/Han/dist/han.min.css?v=3.3">




<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
















  
  
  <link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />







<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/css/main.css?v=5.1.4" rel="stylesheet" type="text/css" />


  <link rel="apple-touch-icon" sizes="180x180" href="/images/favicon.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon.png?v=5.1.4">


  <link rel="mask-icon" href="/images/favicon.png?v=5.1.4" color="#222">





  <meta name="keywords" content="C++," />





  <link rel="alternate" href="/atom.xml" title="平步青云win" type="application/atom+xml" />






<meta name="description" content="学习书籍： 《设计模式：可复用面向对象软件的基础》">
<meta name="keywords" content="C++">
<meta property="og:type" content="article">
<meta property="og:title" content="C++版设计模式">
<meta property="og:url" content="https://zxpgo.github.io/2019/08/02/设计模式/index.html">
<meta property="og:site_name" content="平步青云win">
<meta property="og:description" content="学习书籍： 《设计模式：可复用面向对象软件的基础》">
<meta property="og:locale" content="zh-Hans">
<meta property="og:image" content="https://i.imgur.com/kuX3Z1r.png">
<meta property="og:image" content="https://i.imgur.com/B0F2GPb.png">
<meta property="og:image" content="https://i.imgur.com/x2mVXC4.png">
<meta property="og:image" content="https://i.imgur.com/j1O4LF3.png">
<meta property="og:image" content="https://i.imgur.com/vsCkan9.png">
<meta property="og:image" content="https://i.imgur.com/gkgZZnW.png">
<meta property="og:image" content="https://i.imgur.com/I0eR86U.png">
<meta property="og:image" content="https://i.imgur.com/cRdBUrF.png">
<meta property="og:image" content="https://i.imgur.com/LMEAM9D.png">
<meta property="og:image" content="https://i.imgur.com/v3YxJrG.png">
<meta property="og:image" content="https://i.imgur.com/veFzJtw.png">
<meta property="og:image" content="https://i.imgur.com/KSSRVsX.png">
<meta property="og:image" content="https://i.imgur.com/evwgHu7.png">
<meta property="og:image" content="https://i.imgur.com/GKdJzAC.png">
<meta property="og:image" content="https://i.imgur.com/sqheZSF.png">
<meta property="og:image" content="https://i.imgur.com/HUkUxAR.png">
<meta property="og:image" content="https://i.imgur.com/FcFPMtf.png">
<meta property="og:image" content="https://i.imgur.com/4V8TIZo.png">
<meta property="og:image" content="https://i.imgur.com/w8juT8b.png">
<meta property="og:image" content="https://i.imgur.com/mau83sl.png">
<meta property="og:image" content="https://i.imgur.com/5ndRxkW.png">
<meta property="og:image" content="https://i.imgur.com/T8WmrEK.png">
<meta property="og:image" content="https://i.imgur.com/yJj68Gh.png">
<meta property="og:image" content="https://i.imgur.com/nMQ6Qzm.png">
<meta property="og:image" content="https://i.imgur.com/okHsclo.png">
<meta property="og:image" content="https://i.imgur.com/trfzu5c.png">
<meta property="og:image" content="https://i.imgur.com/pRWHiGV.png">
<meta property="og:image" content="https://i.imgur.com/6pdgMH0.png">
<meta property="og:image" content="https://i.imgur.com/ucRfqWn.png">
<meta property="og:updated_time" content="2020-04-25T09:35:16.028Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="C++版设计模式">
<meta name="twitter:description" content="学习书籍： 《设计模式：可复用面向对象软件的基础》">
<meta name="twitter:image" content="https://i.imgur.com/kuX3Z1r.png">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Gemini',
    version: '5.1.4',
    sidebar: {"position":"right","display":"post","offset":10,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: 'PAO8LM7QB1',
      apiKey: '',
      indexName: 'Blog',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="https://zxpgo.github.io/2019/08/02/设计模式/"/>





  <title>C++版设计模式 | 平步青云win</title>
  





  <script type="text/javascript">
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?7a4517a3ce6d7c50203655d056f01ac3";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>




</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  
  
    
  

  <div class="container sidebar-position-right page-post-detail">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/"  class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">平步青云win</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <h1 class="site-subtitle" itemprop="description"></h1>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-about">
          <a href="/about/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-user"></i> <br />
            
            关于
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/tags/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br />
            
            标签
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/categories/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br />
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
            
            归档
          </a>
        </li>
      
        
        <li class="menu-item menu-item-随笔">
          <a href="/sui" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-question-circle"></i> <br />
            
            随笔
          </a>
        </li>
      

      
        <li class="menu-item menu-item-search">
          
            <a href="javascript:;" class="popup-trigger">
			
          
            
              <i class="menu-item-icon fa fa-search fa-fw"></i> <br />
            
            搜索
          </a>
        </li>
      
    </ul>
  

  
    <div class="site-search">
      
  <div class="popup search-popup local-search-popup">
  <div class="local-search-header clearfix">
    <span class="search-icon">
      <i class="fa fa-search"></i>
    </span>
    <span class="popup-btn-close">
      <i class="fa fa-times-circle"></i>
    </span>
    <div class="local-search-input-wrapper">
      <input autocomplete="off"
             placeholder="搜索..." spellcheck="false"
             type="text" id="local-search-input">
    </div>
  </div>
  <div id="local-search-result"></div>
</div>



    </div>
  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://zxpgo.github.io/2019/08/02/设计模式/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="zxp">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="平步青云win">
    </span>

    
      <header class="post-header">

        
        
          <h2 class="post-title" itemprop="name headline">C++版设计模式</h2>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2019-08-02T22:28:50+08:00">
                2019-08-02
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/C/" itemprop="url" rel="index">
                    <span itemprop="name">C++</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          
             <span id="/2019/08/02/设计模式/" class="leancloud_visitors" data-flag-title="C++版设计模式">
               <span class="post-meta-divider">|</span>
               <span class="post-meta-item-icon">
                 <i class="fa fa-eye"></i>
               </span>
               
                 <span class="post-meta-item-text">阅读次数&#58;</span>
               
                 <span class="leancloud-visitors-count"></span>
             </span>
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body han-init-context" itemprop="articleBody">

      
      

      
        <p>学习书籍： 《设计模式：可复用面向对象软件的基础》<a id="more"></a></p>
<h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><p>数据库有设计模式，架构有设计模式。而这里介绍的面向对象的设计模式。</p>
<p>面对对象机制:</p>
<ul>
<li>封装，隐藏内部实现</li>
<li>继承，复用现有代码</li>
<li>多态，改写对象行为</li>
</ul>
<p>复杂问题处理方法：</p>
<ul>
<li>分解</li>
<li>抽象</li>
</ul>
<p>分解的方法，代码不容易复用。而抽象可以采用一个通用的方法统一处理，所以实现了代码的复用。</p>
<h2 id="设计原则"><a href="#设计原则" class="headerlink" title="设计原则"></a>设计原则</h2><h3 id="重新认识面向对象"><a href="#重新认识面向对象" class="headerlink" title="重新认识面向对象"></a>重新认识面向对象</h3><ul>
<li>隔离变化：从宏观层面来看，面向对象的构建方式更能适用软件的变化，能够将变化所带来的影响降为最小</li>
<li>各司其责：从微观方面来看，面向对象的方式更强调各个类的“责任”，由于需求变化导致的新增类型不应该影响原来类型的实现——就是所谓的各司其责，一般通过采用多态机制实现。</li>
<li>对象是什么<ul>
<li>从语言层面来看，对象封装了代码和数据</li>
<li>从规格层面来讲，对象是一系列可被使用的公共接口</li>
<li>从概念层面来讲，对象是某种拥有责任的抽象</li>
</ul>
</li>
</ul>
<h3 id="面向对象设计原则"><a href="#面向对象设计原则" class="headerlink" title="面向对象设计原则"></a>面向对象设计原则</h3><h4 id="1-依赖导致原则（DIP）"><a href="#1-依赖导致原则（DIP）" class="headerlink" title="1.依赖导致原则（DIP）"></a>1.依赖导致原则（DIP）</h4><ul>
<li>高层模块（稳定）不应该依赖于底层模块（变化），二者都应依赖于抽象（稳定）。</li>
<li>抽象（稳定）不应该依赖于实现细节（变化），实现细节应依赖于抽象（稳定）。</li>
</ul>
<p>MainForm是一个高层模块，Line,Rect是一个底层模块，底层模块可能变化，比如添加Circle类等。MainForm类依赖于Line,Rect等，这样是不好的，因为高层模块依赖底层模块。</p>
<p>另一种，MainForm依赖于一个抽象类Shape，而Line, Rect等也依赖于Shape。这样的设计是好的，二者都依赖于抽象，并且实现细节（Rect,Line,Circle等）依赖于抽象。  </p>
<p>依赖导致原则是是最重要的设计原则，贯穿所有设计摸出。</p>
<h4 id="2-开放封闭原则-OCP"><a href="#2-开放封闭原则-OCP" class="headerlink" title="2.开放封闭原则(OCP)"></a>2.开放封闭原则(OCP)</h4><ul>
<li>对扩展开放，对更改封闭</li>
<li>类模板应该可扩展，但是不可修改</li>
</ul>
<p>比如，有一个桌子不满足消防规定，即材料无法满足一定的防火等级。于是将桌子返回给厂家。第一种厂家，将桌子上不满足防火等级的材料拆掉，并重新制作（修改）；而聪明的厂家会在桌子上刷一层防火物质（扩展）。</p>
<h4 id="3-单一职责原则-SRP"><a href="#3-单一职责原则-SRP" class="headerlink" title="3.单一职责原则(SRP)"></a>3.单一职责原则(SRP)</h4><ul>
<li>一个类应该仅有一个引起它变化的原因。</li>
<li>变化的方向隐含着类的责任。</li>
</ul>
<h4 id="4-LisKov替换原则-LSP"><a href="#4-LisKov替换原则-LSP" class="headerlink" title="4.LisKov替换原则(LSP)"></a>4.LisKov替换原则(LSP)</h4><ul>
<li>子类必须能够替换它们的基类(IS-A)。</li>
<li>继承表达类型抽象。</li>
</ul>
<h4 id="5-接口隔离原则-ISP"><a href="#5-接口隔离原则-ISP" class="headerlink" title="5.接口隔离原则(ISP)"></a>5.接口隔离原则(ISP)</h4><ul>
<li>不应该强迫客户程序依赖它们不用的方法。</li>
<li>接口应该小而完备。</li>
</ul>
<h4 id="6-优先使用对象组合，而不是类继承"><a href="#6-优先使用对象组合，而不是类继承" class="headerlink" title="6.优先使用对象组合，而不是类继承"></a>6.优先使用对象组合，而不是类继承</h4><ul>
<li>类继承通常为“白箱复用”，对象组合通常为“黑箱复用”。</li>
<li>继承在某种程度上破坏了封装性，子类父类耦合度高。</li>
<li>而对象组合则只要求被组合的对象具有良好定义的接口，耦合度低。</li>
</ul>
<p>备注：耦合度是一种软件度量，是指一程序中，模块及模块之间信息或参数依赖的程度。耦合度越高，代码越差，因为一个模块修改，可能会影响多个模块，这是一种不好的设计。</p>
<h4 id="7-封装变化点"><a href="#7-封装变化点" class="headerlink" title="7.封装变化点"></a>7.封装变化点</h4><p>使用封装来创建对象之间的分界层，让设计者可以在分界层的一侧进行修改，而不会对另一侧产生不良的影响，从而实现层次间的松耦合。</p>
<p>从底层思维角度来讲，封装就是封装代码和数据，而从更高层次的理解，封装是封装变化点，一层变化，另一侧稳定。</p>
<h4 id="8-针对接口编程，而不是针对实现编程"><a href="#8-针对接口编程，而不是针对实现编程" class="headerlink" title="8.针对接口编程，而不是针对实现编程"></a>8.针对接口编程，而不是针对实现编程</h4><ul>
<li>不将变量类型声明为某个特定的具体类，而是声明为某个接口。</li>
<li>客户程序无需获知对象的具体类型，只需要知道对象所具有的接口。</li>
<li>减少系统中各部分的依赖关系，从而实现“高内聚，松耦合”的类型设计方案。</li>
</ul>
<h3 id="设计原则提升为设计经验"><a href="#设计原则提升为设计经验" class="headerlink" title="设计原则提升为设计经验"></a>设计原则提升为设计经验</h3><p>设计习语： 描述与特定编程语言相关的底层模式，技巧惯用法</p>
<p>设计模式：主要描述的是“类和相互通信的对象之间的组织关系”，包括它们的角色、职责、写作方式等方面。</p>
<p>架构模式： 描述系统中与基本结构组织关系密切的高层模式，包括子系统划分，职责，以及如何组织它们之间关系的规则。</p>
<h2 id="设计模式"><a href="#设计模式" class="headerlink" title="设计模式"></a>设计模式</h2><p>GOF-23模式分类：</p>
<ul>
<li><p>从目的来看：</p>
<ul>
<li>创建型模式：将对象的部分创建工作，从而应对需求变化为对象创建时具体对象类型实现引来的冲击。</li>
<li>结构性模式：通过类继承或者对象组合获得灵活的结构，从而应对需求变化为对象的结构带来的冲击。</li>
<li>行为型模式：通过类继承或对象组合来划分类与对象间的职责，从而应对需求变化为多个交互的对象带来的冲击。</li>
</ul>
</li>
<li><p>从范围来看：</p>
<ul>
<li>类模式：处理类与子类的静态关系，偏向于继承方式</li>
<li>对象模式：处理对象间的动态关系，偏向于组合方式</li>
</ul>
</li>
<li><p>功能</p>
<ul>
<li>组件协作：<ul>
<li>Template Method 模板方法</li>
<li>Strategy 策略模式</li>
<li>Observer/Event 观察者/事件模式</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code>- 单一职责：

- 对象创建

- 对象性能

- 接口隔离

- 状态变化

- 数据结构

- 行为变化

- 领域问题
</code></pre><p>重构获得模式：</p>
<ul>
<li>面对对象设计模式是“好的面向对象设计”，所谓“好的面向对象设计”指是那些可以满足“应对变化，提供复用”的设计。</li>
<li>现代软件设计的特征是“需求的频繁变化”。设计模式的要点是“寻找变化点，然后在变化点处应用设计模式，从而来更好地应对需要的变化”。“什么时候、什么地方应用设计模式”比“理解设计模式结构本身”更为重要。</li>
<li>设计模式的应用不宜先入为主，一上来就使用设计模式是对设计模式的最大误用。没有一步到位的设计模式。敏捷软件开发实践提倡的”Refactoring to Patterns(重构获得模式)“是目前普遍公认的最好的使用设计模式的方法。首先没有应用设计模式的时候，会存在什么问题，然后一步一步迭代修改，应用设计模式。</li>
</ul>
<p>推荐书籍： 《重构》和 《重构与模式》</p>
<p>重构关键技法：</p>
<ul>
<li>静态 -&gt; 动态</li>
<li>早绑定 -&gt; 晚绑定</li>
<li>继承  -&gt; 组合</li>
<li>编译时依赖 -&gt; 运行时依赖</li>
<li>紧耦合 -&gt; 松耦合</li>
</ul>
<h2 id="组件协作模式"><a href="#组件协作模式" class="headerlink" title="组件协作模式"></a>组件协作模式</h2><p>现在软件专业分工之后的第一个结果是”框架与应用程序的划分“，”组件协作“模式通过晚期绑定，来实现框架与应用程序之间的松耦合，是二者之间协作时常用的模式。</p>
<p>典型模式：</p>
<ul>
<li>Template Method 模板方法</li>
<li>Strategy 策略方式</li>
<li>Observer/Event 观察者/事件</li>
</ul>
<p>这三种是典型的组件协作模式，其他模式也有组件协作。</p>
<h3 id="Tempalte-Method-模板方法"><a href="#Tempalte-Method-模板方法" class="headerlink" title="Tempalte Method(模板方法)"></a>Tempalte Method(模板方法)</h3><h4 id="动机"><a href="#动机" class="headerlink" title="动机"></a>动机</h4><p>在软件构建过程中，对于某一项任务，它常常有稳定的整体操作结构，但各个子步骤却有很多改变需求，或者由于固有的原因（比如框架与应用之间的关系）而无法和任务的整体结构同时实现。</p>
<p>如何在确定稳定操作结构的前提下，来灵活应对各个子步骤的变化或晚期实现需求？</p>
<p>第一种写法：</p>
<pre><code>class Library{
public:
    void Step1(){ //稳定
        //...
    }
    void Step3(){//稳定
        //...
    }
    void Step5(){//稳定
        //...
    }
};

class Application{
public:
    bool Step2(){
        //...
    }
    void Step4(){
        //...
    }
};

int main()
{
    Library lib();
    Application app();

    lib.Step1();

    if (app.Step2()){  //支持变化 ==&gt; 虚函数的多态调用
        lib.Step3();
    }

    for(int i = 0; i &lt; 4; i++){
        app.Step4();//支持变化 ==&gt; 虚函数的多态调用
    }

    lib.Step5();
};
</code></pre><p>改善写法(Template Method)：</p>
<pre><code>class Library{
public:
    //稳定 template method
    void Run()
    {
        Step1();

        if (Step2()){  //支持变化 ==&gt; 虚函数的多态调用
            Step3();
        }

        for(int i = 0; i &lt; 4; i++){
            Step4();//支持变化 ==&gt; 虚函数的多态调用
        }

        Step5();
    }
    virtual ~Library() {};
protected:
    void Step1(){ //稳定
        //...
    }
    void Step3(){//稳定
        //...
    }
    void Step5(){//稳定
        //...
    }
    virtual bool Step2() = 0; //变化
    virtual void Step4() = 0; //变化        
}; 

class Application : public Library{
protected: 
    virtual bool Step2(){
        //...子类重写实现
    }
    virtual void Step4(){
        //...子类重写实现
    }
};

int main()
{
    Library * pLib = new Application();
    lib-&gt;run();

    delete pLib;
}
</code></pre><p>结构化软件设计流程：</p>
<p><img src="https://i.imgur.com/kuX3Z1r.png" alt=""></p>
<p>第一种写法就是上述流程实现。</p>
<p>Template method写法：</p>
<p>将Applicatoin开发人员所写的程序主流程放到Library中。</p>
<p>第一种是Application（晚）调用Library（早）（早绑定）。第二种是Library（早）调用Appliction（晚）（虚函数调用子类的实现）（晚绑定）。</p>
<h4 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h4><p>定义一个操作中的算法的骨架（稳定），而将一些步骤延迟（变化）到子类中。Template Method使得子类可以不改变（复用）一个算法的结构即可重新定义（override重写）该算法的某些特定步骤。</p>
<p>其中的骨架就是run()函数，一些步骤（变化）就是Step2()和Step4()，这些方法在子类中可以重写。子类可以复用run()算法结构。</p>
<p>稳定的代码写成非虚函数，非稳定（变化）的代码写成虚函数（纯虚函数）。</p>
<p>我们假定run()是稳定的，如果run()不稳定，经常变化，就不适用Template method。所有东西都不稳定，所有的模式都没有意义，或者所有东西都稳定，所有模式也都没有意义。设计模式应用在变化与稳定之中，寻找变化点。</p>
<p>C语言时代，程序员经常需要写一个框架（程序主流程），而在C++时代，采用了Template method，这个框架被library开发者已经写好，只需要写几个步骤。</p>
<p>设计结构：</p>
<p><img src="https://i.imgur.com/B0F2GPb.png" alt=""></p>
<p>其中TemplateMethod()是稳定的，而PrimitiveOperation1()和PrimitiveOperation2()是变化的。</p>
<p>总结：</p>
<ul>
<li>Template Method模式是一种非常基础性的设计模式，在面对对象系统中有着大量的应用。它用最简洁的机制（虚函数的多态性）为很多应用程序框架提供了灵活的扩展点，是代码复用方法的基本实现结构。</li>
<li>除了可以灵活应对子步骤的变化外，“不要调用我，让我来调用你”的反向控制结构是Template Method的典型应用。</li>
<li>在具体实现方面，被Template Method调用的虚方法可以有实现，也可以没有任何是咩（抽象方法、纯虚方法），但一般它们设计为protect方法（这些方法单独作为一个方法没有意义，放在一个流程中才有意义）。</li>
</ul>
<p>实现晚绑定机制的手段：</p>
<ul>
<li>虚函数（虚函数本质也是函数指针实现的）</li>
<li>函数指针（C语言时代就有）</li>
</ul>
<h3 id="Strategy-策略模式"><a href="#Strategy-策略模式" class="headerlink" title="Strategy(策略模式)"></a>Strategy(策略模式)</h3><h4 id="动机-1"><a href="#动机-1" class="headerlink" title="动机"></a>动机</h4><ul>
<li><p>在软件构建过程中，某些对象使用的算法可能多种多样，经常改变，如果将这些算法都编码到对象中，将会使对象变得异常复杂，而且有时候指支持不使用的算法也是一个性能负担。</p>
</li>
<li><p>如何在运行时根据需要透明低更改对象的算法？将算法与对象本身解耦，从而避免上述问题？</p>
</li>
</ul>
<p>场景：计算每个国家的税收。</p>
<pre><code>enum TaxBase {
    CN_Tax,
    US_Tax,
    DE_Tax,
};

class SalesOrder {
    TaxBase tax;
public:
    double CalculateTax() {
        if (tax == CN_Tax) {
            //CN
        }
        else if (tax == US_Tax) {
            //US
        }
        else if (tax == DE_Tax) {
            //DE...
        }
        //...
    }
};
</code></pre><p>如果未来需要指出日本，法国的税收计算。就需要修改枚举类型，并对CalculateTax方法修改，这就违反了开发封闭原则。而应该采用扩展的方式，而不是修改。</p>
<pre><code>class TaxStrategy {
public:
    virtual double Calculate(const Context&amp; context) = 0;
    virtual ~TaxStrategy() {}
};

class CNTax : public TaxStrategy {
public:
    virtual double Calculate(const Context&amp; context) {
        //CN
    }
};

class USTax : public TaxStrategy {
public:
    virtual double Calculate(const Context&amp; context) {
        //US
    }
};

class DETax : public TaxStrategy {
public:
    virtual double Calculate(const Context&amp; context) {
        //DE
    }
};

class SalesOrder {
private:
    TaxStrategy * strategy;
public:
    SalesOrder(strategyFactory* strategyFactory) {
        this-&gt;strategy = strateyFactory;
    }
    ~SalesOrder() {
        delete this-&gt;strategy;
    }
    double CalculateTax() {
        //...
        Context context;

        double val = strategy-&gt;Calculate(Context); //多态调用
        //...
    }
};
</code></pre><p>对于上述方法，如果需要添加计算法国的税收。只需要添加一个类继承TaxStrategy（扩展）。不需要修改任何内容（得到了复用），这就符合了开放封闭原则。</p>
<pre><code>class FRTax : public TaxStrategy {
public:
    virtual double Calculate(const Context&amp; context) {
        //DE
    }
};
</code></pre><h4 id="定义-1"><a href="#定义-1" class="headerlink" title="定义"></a>定义</h4><p>定义一系列算法，把它们一个个封装起来，并且使它们可相互替换（变换）。该模式使得算法可独立于使用它的客户程序（稳定）而变化（扩展，子类化）。</p>
<p>设计结构：</p>
<p><img src="https://i.imgur.com/x2mVXC4.png" alt=""></p>
<p>红色框内的是稳定的，而蓝色框内的是变化的（可以增加各个国家的税收计算）。</p>
<p>要点总结：</p>
<ul>
<li>Strategy及其子类为组件提供了一系列可重用的算法，从而可以使得类型在<strong>运行时</strong>方便地根据需要在各个算法之间进行切换。</li>
<li>Strategy模式提供了条件判断语句以外的另一种选择，消除条件判断语句，就是在解耦合。含有许多条件判断语句的代码通过都需要Strategy。</li>
<li>如果Strategy对象没有实例变量，那么各个上下文可以共享同一个Strategy对象，从而节省对象开销。</li>
</ul>
<p>出现if,else语句，就是使用Strategy模式的特征。不推荐使用ifelse，这是结构化时代的分而治之的思想，而应该采用抽象思维。</p>
<p>当然有时候ifelse无法适用于Strategy，即ifelse绝对不变的情况，比如根据性别来判断，男女是绝对不变的，所以无法使用Strategy；还有就是一周七天，也是固定不变的，就不适用于Strategy。switch跟ifelse相同。</p>
<h3 id="Observer-观察者模式"><a href="#Observer-观察者模式" class="headerlink" title="Observer(观察者模式)"></a>Observer(观察者模式)</h3><h4 id="动机-2"><a href="#动机-2" class="headerlink" title="动机"></a>动机</h4><ul>
<li>在软件构建过程中，我们需要为某些对象建立一种“通知依赖关系”———一个对象（目标对象）的状态发生改变，所有的依赖对象（观察者对象）都将得到通知。如果这种的依赖关系过于紧密，将使软件不能很好地抵御变化。</li>
<li>使用面向对象计数，可以将这种依赖关系弱化，并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。</li>
</ul>
<p>场景：将大文件分割成多个小文件。</p>
<pre><code>class MainForm : public Form
{
    TextBox * txtFilePath;
    TextBox * txtFileNumber;
public:
    void Button1_Click() {
        string filePath = txtFilePath-&gt;getText();
        int number = atoi(txtFileNumber-&gt;getText()).c_str();

        FileSplitter splitter(filePath, number);

        splitter.split();
    }
};

class FileSplitter {
    string m_filePath;
    int m_fileNumber;
public:
    FileSplitter(const string&amp; filePath, int fileNumber) : 
        m_filePath(filePath),m_fileNumber(fileNumber){
    }

    void split() {
        //1.读取大文件
        //2. 分批次先小文件中写入
        for (int i = 0; i &lt; m_fileNumber; i++) {
            //...
        }
    }
};
</code></pre><p>先其中添加一个进度条，显示分隔进度：</p>
<pre><code>class MainForm : public Form
{
    TextBox * txtFilePath;
    TextBox * txtFileNumber;
    ProgressBar * progressBar;
public:
    void Button1_Click() {
        string filePath = txtFilePath-&gt;getText();
        int number = atoi(txtFileNumber-&gt;getText()).c_str();

        FileSplitter splitter(filePath, number, progressBar);

        splitter.split();
    }
};

class FileSplitter {
    string m_filePath;
    int m_fileNumber;
    ProgressBar * m_progressBar;
public:
    FileSplitter(const string&amp; filePath, int fileNumber, ProgressBar * progressBar) :
        m_filePath(filePath),m_fileNumber(fileNumber), m_progressBar(progressBar){
    }

    void split() {
        //1.读取大文件
        //2. 分批次先小文件中写入
        for (int i = 0; i &lt; m_fileNumber; i++) {
            //...
            if (m_progressBar != NULL)
                m_progressBar-&gt;setVaule((i+1)/m_txtFileNumber); //更新进度条
        }
    }
};
</code></pre><p>上面这种进度条是基于GUI，但是有时候需要显示百分比，或者在Linux下打印“-”等等形式，这样就需要修改FileSpliteer类。这就违反了依赖倒置原则，m_progressBar不应该是一个具体的对象，而应该是一个接口。</p>
<pre><code>class MainForm : public Form, public IProgress
{
    TextBox * txtFilePath;
    TextBox * txtFileNumber;
    ProgressBar * progressBar;

public:
    void Button1_Click() {
        string filePath = txtFilePath-&gt;getText();
        int number = atoi(txtFileNumber-&gt;getText()).c_str();

        FileSplitter splitter(filePath, number, this);

        splitter.split();
    }

    void DoProgress(float value){
        progressBar-&gt;value(value);
    }
};
</code></pre><p>​<br>​    class IProgress {<br>​        virtual void DoProgress(float value) = 0;<br>​        virtual ~IProgress() {}<br>​    };<br>​    class FileSplitter {<br>​        string m_filePath;<br>​        int m_fileNumber;<br>​        //ProgressBar <em> m_progressBar; //具体通知控件<br>​        IProgress </em> m_iprogress; //抽象通知机制<br>​    public:<br>​        FileSplitter(const string&amp; filePath, int fileNumber, IProgress * iprogress) :<br>​            m_filePath(filePath), m_fileNumber(fileNumber), m_iprogress(iprogress) {<br>​        }<br>​<br>        void split() {<br>            //1.读取大文件</p>
<pre><code>        //2. 分批次先小文件中写入
        for (int i = 0; i &lt; m_fileNumber; i++) {
            //...
            //m_progressBar-&gt;setVaule();
            float progressValue = m_fileNumber;
            progressValue = (i + 1) / progressValue;
            onProgress(progressValue);
        }
    }
protected:
    void onProgress(float vaule) {
        if (m_iprogress != nullptr) {
            m_iprogress-&gt;DoProgress(vaule); //更新进度条
        }
    }
};
</code></pre><p>这是上面的一个观察者，仅支持一个通知。下面的代码可以支持多个观察者：</p>
<pre><code>class MainForm : public Form, public IProgress
{
    TextBox * txtFilePath;
    TextBox * txtFileNumber;
    ProgressBar * progressBar;

public:
    void Button1_Click() {
        string filePath = txtFilePath-&gt;getText();
        int number = atoi(txtFileNumber-&gt;getText()).c_str();

        FileSplitter splitter(filePath, number);
        ConsoleNotifer cn;
        splitter.addIProgressList(this);
        splitter.addIProgressList(&amp;cn);

        splitter.split();
    }

    void DoProgress(float value) {
        progressBar-&gt;value(value);
    }
};
class ConsoleNotifer : public IProgress {
public:
    virtual void DoProgress(float value) {
        cout &lt;&lt; &quot;.&quot;;
    }
};

class IProgress {
    virtual void DoProgress(float value) = 0;
    virtual ~IProgress() {}
};
class FileSplitter {
    string m_filePath;
    int m_fileNumber;
    //ProgressBar * m_progressBar; //具体通知控件
    List&lt;IProgress*&gt; m_iprogressList; //抽象通知机制,支持多个观察者
public:
    FileSplitter(const string&amp; filePath, int fileNumber) :
        m_filePath(filePath), m_fileNumber(fileNumber) {
    }
    void addIProgressList(IProgress * progress) {
        m_iprogressList.add(progress);
    }
    void removeProgressList(IProgress * progress){
        m_iprogressList.remove(progress);
    }
    void split() {
        //1.读取大文件

        //2. 分批次先小文件中写入
        for (int i = 0; i &lt; m_fileNumber; i++) {
            //...
            //m_progressBar-&gt;setVaule();
            float progressValue = m_fileNumber;
            progressValue = (i + 1) / progressValue;
            onProgress(progressValue);
        }
    }
protected:
    void onProgress(float vaule) {
        auto ite = m_iprogressList.begin();
        while (ite != m_iprogressList.end())
        {
            (*ite)-&gt;DoProgress(vaule); //更新进度条
            ite++;
        }

    }
};
</code></pre><h4 id="定义-2"><a href="#定义-2" class="headerlink" title="定义"></a>定义</h4><p>定义对象间的一种一对多（变化）的依赖关系，以便当一个对象（Subject）的状态发生变化时，所有依赖于它的对象都得到通知并自动更新。</p>
<p>设计结构：</p>
<p><img src="https://i.imgur.com/j1O4LF3.png" alt=""></p>
<p>Observer对应IProgress, Update()相等于DoProgress()。Attach(Observer)相等于addIProgressList(IProgress*)，Detach相等于removeProgressList，只不过上面程序没有分开成一个单独的基类，Notify()相等于OnProgress()。ConcreteSubject就是上面的FileSplitter。ConCreteObserver就是MainForm或者ConsoleNofier，即一个具体的观察者。有时候，将Subject和ConcreteSubject放在一个类中，上述代码就是这样。</p>
<p>要点总结：</p>
<ul>
<li>使用面向对象的抽象，Observer模式使得我们可以独立地改变目标与观察者，从而使二者之间的依赖关系达到松耦合。</li>
<li>目标发送通知时，无需指定观察者，通知（可以携带通知信息作为参数）会自动传播。</li>
<li>观察者自己决定是否需要订阅通知，目标对象对此一无所知。</li>
<li>Observer模式是基于事件的UI框架中非常常用的设计模式，也是MVC模式的一个重要组成部分。</li>
</ul>
<h2 id="单一职责模式"><a href="#单一职责模式" class="headerlink" title="单一职责模式"></a>单一职责模式</h2><p>在软件组件的设计中，如果责任划分不清晰，使用继承得到的结果往往是随着需要的变化，子类急剧膨胀，同时充斥着重复代码，这时候的关键是划清责任。</p>
<p>典型模式：</p>
<ul>
<li>Decorator</li>
<li>Bridge</li>
</ul>
<p>这两个模式在单一职责上表现得特别明显，其他模式也可能具有单一职责特性。</p>
<h3 id="Decorate-装饰模式"><a href="#Decorate-装饰模式" class="headerlink" title="Decorate(装饰模式)"></a>Decorate(装饰模式)</h3><h4 id="动机-3"><a href="#动机-3" class="headerlink" title="动机"></a>动机</h4><ul>
<li>在某些情况下，我们可能会“过度地使用继承来扩展对象的功能”，由于继承为类型引入的静态特质，使得这种扩展方式缺乏灵活性；并且随着子类的增多（扩展功能的增多），各种子类的组合（扩展功能的组合）会导致更多子类的膨胀。</li>
<li>如何使“对象功能的扩展”能够根据需要来动态地实现？同时避免“扩展功能的增多”带来的子类膨胀问题？从而使得任何“功能扩展变化”所导致的影响降为最低？</li>
</ul>
<p>场景：流操作。</p>
<p><img src="https://i.imgur.com/vsCkan9.png" alt=""></p>
<p>​<br>​    class Stream {<br>​    public:<br>​        virtual char Read(int number) = 0;<br>​        virtual void Seek(int position) = 0;<br>​        virtual void Write(char data) = 0;<br>​        virtual ~Stream(){}<br>​    };<br>​<br>    class FileStream : public Stream<br>    {<br>    public:<br>        virtual char Read(int number) {<br>            //读文件流<br>        }<br>        virtual void Seek(int position) {<br>            //定位文件流<br>        }<br>        virtual void Write(char data) {<br>            //写文件流<br>        }<br>    };</p>
<pre><code>class NetWorkStream : public Stream {
public:
    virtual char Read(int number) {
        //读网络流
    }
    virtual void Seek(int position) {
        //定位网络流
    }
    virtual void Write(char data) {
        //写网络流
    }
};
</code></pre><p>​<br>​    class MemoryStream : public Stream {<br>​    public:<br>​        virtual char Read(int number) {<br>​            //读内存流<br>​        }<br>​        virtual void Seek(int position) {<br>​            //定位内存流<br>​        }<br>​        virtual void Write(char data) {<br>​            //写内存流<br>​        }<br>​    };<br>​<br>    class CryptoFileStream : public FileStream {<br>    public:<br>        virtual char Read(int number) {<br>            //额外的加密操作<br>            FileStream::Read(number);//读文件流<br>        }<br>        virtual void Seek(int position) {<br>            //额外的加密操作<br>            FileStream::Seek(position);//定位文件流<br>            //额外的加密操作<br>        }<br>        virtual void Write(char data) {<br>            //额外的加密操作<br>            FileStream::Write(data);//定位文件流<br>            //额外的加密操作<br>        }<br>    };</p>
<p>​<br>​    class CryptoNetWorkStream : public NetWorkStream {<br>​    public:<br>​        virtual char Read(int number) {<br>​            //额外的加密操作<br>​            NetWorkStream::Read(number);//读文件流<br>​        }<br>​        virtual void Seek(int position) {<br>​            //额外的加密操作<br>​            NetWorkStream::Seek(position);//定位文件流<br>​            //额外的加密操作<br>​        }<br>​        virtual void Write(char data) {<br>​            //额外的加密操作<br>​            NetWorkStream::Write(data);//定位文件流<br>​            //额外的加密操作<br>​        }<br>​    };<br>​<br>    class CryptoMemoryStream : public MemoryStream {<br>    public:<br>        virtual char Read(int number) {<br>            //额外的加密操作<br>            MemoryStream::Read(number);//读文件流<br>        }<br>        virtual void Seek(int position) {<br>            //额外的加密操作<br>            MemoryStream::Seek(position);//定位文件流<br>            //额外的加密操作<br>        }<br>        virtual void Write(char data) {<br>            //额外的加密操作<br>            MemoryStream::Write(data);//定位文件流<br>            //额外的加密操作<br>        }<br>    };</p>
<pre><code>//缓冲
class BufferedFileStrem : public FileStream {
    //...
};
class BufferedNetWorkStrem : public FileStream {
    //...
};
class BufferedMemoryStrem : public FileStream {
    //...
};

//即缓冲又加密
class CryptoBufferedFileStream : public FileStream {
public :
    virtual char Read(int number)
    {
        //额外的加密操作
        //额外的缓冲操作
        FileStream::Read(number); //读文件流
    }
    //...
};
class CryptoBufferedNetworkStream : public NetWorkStream {
public:
    virtual char Read(int number)
    {
        //额外的加密操作
        //额外的缓冲操作
        NetWorkStream::Read(number); //读文件流
    }
    //...
}; class CryptoBufferedMemoryStream : public MemoryStream {
public:
    virtual char Read(int number)
    {
        //额外的加密操作
        //额外的缓冲操作
        MemoryStream::Read(number); //读文件流
    }
    //...
};

void Process() {
    CryptoFileStream * fs1 = new CryptoFileStream();
    BufferedFileStrem * fs2 = new BufferedFileStrem();
    CryptoBufferedFileStream * fs3 = new CryptoBufferedFileStream();
}
</code></pre><p>第二版：</p>
<pre><code>class Stream {
public:
    virtual char Read(int number) = 0;
    virtual void Seek(int position) = 0;
    virtual void Write(char data) = 0;
    virtual ~Stream(){}
};

class FileStream : public Stream
{
public:
    virtual char Read(int number) {
        //读文件流
    }
    virtual void Seek(int position) {
        //定位文件流
    }
    virtual void Write(char data) {
        //写文件流
    }
};

class NetWorkStream : public Stream {
public:
    virtual char Read(int number) {
        //读网络流
    }
    virtual void Seek(int position) {
        //定位网络流
    }
    virtual void Write(char data) {
        //写网络流
    }
};
</code></pre><p>​<br>​    class MemoryStream : public Stream {<br>​    public:<br>​        virtual char Read(int number) {<br>​            //读内存流<br>​        }<br>​        virtual void Seek(int position) {<br>​            //定位内存流<br>​        }<br>​        virtual void Write(char data) {<br>​            //写内存流<br>​        }<br>​    };<br>​<br>    //扩展操作<br>    class CryptoStream : public Stream{<br>        Stream <em> stream;//…<br>    public:<br>        CryptoStream(Stream </em> stream) {<br>            this-&gt;stream = stream;<br>        }<br>        virtual char Read(int number) {<br>            //额外的加密操作<br>            stream-&gt;Read(number);//读文件流<br>        }<br>        virtual void Seek(int position) {<br>            //额外的加密操作<br>            stream-&gt;Seek(position);//定位文件流<br>            //额外的加密操作<br>        }<br>        virtual void Write(char data) {<br>            //额外的加密操作<br>            stream-&gt;Write(data);//定位文件流<br>            //额外的加密操作<br>        }<br>    };</p>
<p>​<br>​    //缓冲<br>​    class BufferedStream : public Stream {<br>​        Stream <em> stream;<br>​        //…<br>​    public:<br>​        BufferedStream(Stream </em> stm) :stream(stm) {<br>​<br>        }<br>    };</p>
<pre><code>void Process() {
    //运行时装配
    FileStream * s1 = new FileStream();
    CryptoStream * s2 = new CryptoStream(s1); //加密
    BufferedStream * s3 = new BufferedStream(s1); //缓冲
    BufferedStream * s4 = new BufferedStream(s2); //即加密又缓冲
}
</code></pre><p>第三步，添加一个中间层，装饰层</p>
<pre><code>class Stream {
public:
    virtual char Read(int number) = 0;
    virtual void Seek(int position) = 0;
    virtual void Write(char data) = 0;
    virtual ~Stream(){}
};

class FileStream : public Stream
{
public:
    virtual char Read(int number) {
        //读文件流
    }
    virtual void Seek(int position) {
        //定位文件流
    }
    virtual void Write(char data) {
        //写文件流
    }
};

class NetWorkStream : public Stream {
public:
    virtual char Read(int number) {
        //读网络流
    }
    virtual void Seek(int position) {
        //定位网络流
    }
    virtual void Write(char data) {
        //写网络流
    }
};
</code></pre><p>​<br>​    class MemoryStream : public Stream {<br>​    public:<br>​        virtual char Read(int number) {<br>​            //读内存流<br>​        }<br>​        virtual void Seek(int position) {<br>​            //定位内存流<br>​        }<br>​        virtual void Write(char data) {<br>​            //写内存流<br>​        }<br>​    };<br>​<br>    //扩展操作</p>
<pre><code>class DecoratorStream : public Stream{
protected:
    Stream * stream;
public:
    DecorateStream(Stream * stm) {
        stream = stm;
    }
};
class CryptoStream : public DecoratorStream {
public:
    CryptoStream(Stream * stream):DecoratorStream(stream){
    }
    virtual char Read(int number) {
        //额外的加密操作
        stream-&gt;Read(number);//读文件流
    }
    virtual void Seek(int position) {
        //额外的加密操作
        stream-&gt;Seek(position);//定位文件流
        //额外的加密操作
    }
    virtual void Write(char data) {
        //额外的加密操作
        stream-&gt;Write(data);//定位文件流
        //额外的加密操作
    }
};
</code></pre><p>​<br>​    //缓冲<br>​    class BufferedStream : public DecoratorStream {<br>​<br>        //…<br>    public:<br>        BufferedStream(Stream * stm) :DecoratorStream(stm) {</p>
<pre><code>    }
};

void Process() {
    //运行时装配
    FileStream * s1 = new FileStream();
    CryptoStream * s2 = new CryptoStream(s1); //加密
    BufferedStream * s3 = new BufferedStream(s1); //缓冲
    BufferedStream * s4 = new BufferedStream(s2); //即加密又缓冲
}
</code></pre><p>第三版类的关系图：</p>
<p><img src="https://i.imgur.com/gkgZZnW.png" alt=""></p>
<p>第一个版本中全部使用继承，所以导致类数量规模特别大。而第二版和第三版我们改成使用对象组合，引出多态变化，使得类数量大大减少。</p>
<h4 id="定义-3"><a href="#定义-3" class="headerlink" title="定义"></a>定义</h4><p>动态（组合）地给一个对象增加一些额外的职责。就增加功能而言，Decorator模式比生成子类（继承）更为灵活（消除重复代码，并减少子类个数）</p>
<p>设计结构：</p>
<p><img src="https://i.imgur.com/I0eR86U.png" alt=""></p>
<p>Component类对应我们代码中是Stream类，Decorator类对应位DecoratorStream类，ConcreteComponent类对应为FileStream类、MemoryStream类和NetWorkStream类，ConcreterDecoratorA和ConcreterDecoratorB类对应为CryptoStream类和BufferedStream类。</p>
<p>其中红色框内的类是稳定的，而蓝色框内的类是变化的。</p>
<p>总结</p>
<ul>
<li>通过采用组合而非继承的手法，Decorator模式实现了在运行是动态扩展对象功能的能力，而且可以根据需要扩展多个功能。避免了使用继承带来的“灵活性差”和“多子类衍生问题”。</li>
<li>Decorator类在接口上表现为is-a Component的继承关系，即Decorator类继承了Component类所具有的接口。但在实现上又表现为has-a Component的组合关系，即Decorator类又使用了另外一个Component类。</li>
<li>Decorator模式的目的并非解决“多子类衍生的多继承”问题，Decorator模式应用的要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义。</li>
</ul>
<h3 id="Bridge-桥模式"><a href="#Bridge-桥模式" class="headerlink" title="Bridge(桥模式)"></a>Bridge(桥模式)</h3><h4 id="动机-4"><a href="#动机-4" class="headerlink" title="动机"></a>动机</h4><ul>
<li>由于某些类型的固有的实现逻辑，使得它们具有两个变化的维度，乃至多个维度的变化。</li>
<li>如何应对这种”多维度的变化“？如何利用面向对象技术来使得类型可以轻松地沿着两个乃至多个方面变化，而不引入额外的复杂度？</li>
</ul>
<h4 id="定义-4"><a href="#定义-4" class="headerlink" title="定义"></a>定义</h4><p>将抽象部分（业务功能）与实现部分（平台实现）分离，使它们都可以独立地变化。</p>
<p><img src="https://i.imgur.com/cRdBUrF.png" alt=""></p>
<p>总结：</p>
<ul>
<li>Bridge模式使用”对象间得组合关系“解耦了抽象和实现之间固有得绑定关系，使得抽象和实现可以沿着各自得维度来变化。所谓抽象和实现沿着各自维度的变化，即”子类化“它们。</li>
<li>Bridge模式有时候类似于多继承方法，但是多继承方法往往违背单一职责（即一个类只有一个变化的原因），复用性比较差。Bridge模式是比多继承方法更好的解决办法。</li>
<li>Bridge模式的应用一般在”两个非常强的变化维度“，有时一个类也有多于两个的变化维度，这是可以使用Bridge的扩展模式。</li>
</ul>
<h2 id="对象创建模式"><a href="#对象创建模式" class="headerlink" title="对象创建模式"></a>对象创建模式</h2><p>通过”对象创建”模式绕开new，来避免对象创建（new）过程中所导致的紧耦合（依赖具体类），从而支持对象创建的稳定。它是接口抽象之后的第一步工作。</p>
<p>典型模式：</p>
<ul>
<li>Factory Method: 工厂方法</li>
<li>Abstract Factory：抽象工厂</li>
<li>Prototype：原型</li>
<li>Builder：构建器</li>
</ul>
<h3 id="Factory-Method（工厂方法模式）"><a href="#Factory-Method（工厂方法模式）" class="headerlink" title="Factory Method（工厂方法模式）"></a>Factory Method（工厂方法模式）</h3><ul>
<li>在软件系统中，经常面临着创建对象的工作；由于需求的变化，需要创建对象的具体类型经常变化。</li>
<li>如何应对这种变化？如何绕过常规的对象创建方法（new），提供一种”封装机制“来避免客户程序和这种”具体对象创建工作“的紧耦合？</li>
</ul>
<p>定义：</p>
<p>定义一个用户创建对象的接口，让子类来决定实例化哪一个类。Factory Method使得一个类的实例化延迟（目的：解耦，手段：虚函数）到子类。</p>
<p>创建对象的接口： SplitterFactory</p>
<p>子类： BinarySplitterFactory</p>
<p><img src="https://i.imgur.com/LMEAM9D.png" alt=""></p>
<p>Product: ISplitter</p>
<p>ConcreteProduct: BinarySplitter …</p>
<p>Creator : SplitterFactory</p>
<p>ConcreteCreator: BinarySplitterFactory …</p>
<p>蓝色是变化的，红色是稳定的。</p>
<p>总结：</p>
<ul>
<li>Factory Method模式用于隔离类对象的使用者和具体类型之间的耦合关系。面对一个经常变化的具体类型，紧耦合关系（new）会导致软件的脆弱。</li>
<li>Factory method模式通过面向对象的手法，将索要创建的具体对象工作延迟到子类，从而实现一种扩展（而非更改）的策略，较好地解决了这种耦合关系。</li>
<li>Factory Mathod模式解决“单个对象”的需要变化。缺点在于要创建方法/参数相同。</li>
</ul>
<h3 id="Abstract-Factory抽象工厂"><a href="#Abstract-Factory抽象工厂" class="headerlink" title="Abstract Factory抽象工厂"></a>Abstract Factory抽象工厂</h3><ul>
<li>在软件系统中，经常面临着“一系列相互依赖的对象”的创建工作；同时，由于需求的变化，往往存在更多系列对象的创建工作。</li>
<li>如何应对这种变化？如何绕过常规的对象创建方法（new），提供一种“封装机制”来避免客户程序和这种“多系列具体对象创建工作”的紧耦合？</li>
</ul>
<p>定义：</p>
<p>提供一个接口，让该接口负责创建一系列“相关或者相互依赖的对象”，无需指定它们具体的类。</p>
<p>结构：</p>
<p><img src="https://i.imgur.com/v3YxJrG.png" alt=""></p>
<p>AbstractFactory： IDBFactory</p>
<p>ConcreteFactory1: SqlDBFactory</p>
<p>ConCreteFactoyr2: OracleDBFactory</p>
<p>AbstractProductA: IDBConnection</p>
<p>AbstractProductB: IDBCommand</p>
<p>ProductA1: SqlConnection  ProductA2: OracleConnection</p>
<p>ProductB1: SqlCommand     ProductB2: Oraclecommand</p>
<p>总结：</p>
<ul>
<li>如果没有应对“多系列对象构建”的需求变化，则没有必要使用Abstract Factory模式，这时候使用简单的工厂完全可以。</li>
<li>”系列对象“指的是在某一个特定系列下的对象之间有相互依赖、或作用的关系。不同系列的对象之间不能相互依赖。</li>
<li>Abstract Factory模式主要在于应对”新系列“的需求变动。其缺点在于难以应对”新对象“的需求变动。</li>
</ul>
<h3 id="Prototype（原型模式）"><a href="#Prototype（原型模式）" class="headerlink" title="Prototype（原型模式）"></a>Prototype（原型模式）</h3><ul>
<li>在软件系统中，经常面临着”某些结构复杂的对象“的创建工作；由于需求的变化，这些对象经常面临着剧烈的变化，但是它们却拥有比较稳定一致的接口。</li>
<li>如何应对这种变化？如何向”客户程序（使用这些对象的程序）“隔离出”这些易变对象“，从而使得”依赖这些易变对象的客户程序“不随着需求改变而改变？</li>
</ul>
<p>定义：</p>
<p>使用原型实例指定创建对象的种类，然后通过拷贝这些原型来创建新的对象。</p>
<p><img src="https://i.imgur.com/veFzJtw.png" alt=""></p>
<p>Protoype: ISpillter</p>
<p>ConcretePrototype1: BinarySplitterFacory</p>
<p>总结：</p>
<ul>
<li>Prototype模式同样用于隔离类对象的使用者和具体类型（易变类）之间的耦合关系，它同样要求这些”易变类“拥有”稳定的接口“。</li>
<li>Prototype模式对于”如何创建易变类的实体对象“采用”原型克隆“的方法来做，它使得我们可以非常灵活地动态创建”拥有某些稳定接口“的新对象——所需工作仅仅是注册一个新类的对象（即原型），然后在任何需要的地方Clone。</li>
<li>Prototype模式中的Clone方法可以利用某些框架中的序列化来实现深拷贝。</li>
</ul>
<h3 id="Builder（构建器）"><a href="#Builder（构建器）" class="headerlink" title="Builder（构建器）"></a>Builder（构建器）</h3><ul>
<li>在软件系统中，有时候面临着”一个复杂对象“的创建工作，其通常由各个部分的子对象用一定的算法构成；由于需求的变化，这个复杂对象的各个部分经常面临着剧烈的变化，但是将它们组合在一起的算法相对稳定。</li>
<li>如何应对这种变化？如何提供一种”封装机制“来隔离出”复杂对象的各个部分“的变化，从而保持系统中的”稳定构建算法“不随着需求改变而改变？</li>
</ul>
<p>定义：</p>
<p>将一个复杂对象的构建与其表示相分离，使得同样的构建过程（稳定）可以创建不同的表示（变化）。</p>
<p><img src="https://i.imgur.com/KSSRVsX.png" alt=""></p>
<p>总结：</p>
<ul>
<li>Builder模式主要用于”分步骤构建一个复杂的对象“。在这其中”分步骤“是一个稳定的算法，而复杂对象的各个部分则经常变化。</li>
<li>变化点在哪里，封装哪里——Builder模式主要在于应对”复杂对象各个部分“的频繁需求变动。其缺点在于难以应付”分步骤构建算法“的需求变动。</li>
<li>在Builder模式中，要注意不同的语言中构造器内调用虚函数的差别。</li>
</ul>
<h2 id="对象性能模式"><a href="#对象性能模式" class="headerlink" title="对象性能模式"></a>对象性能模式</h2><p>面向对象很好地解决了“抽象”的问题，但是必不可免地要付出一定的代价。对于通常情况来讲，面向对象的成本大都可以忽略不计。但是某些情况，面向对象所带来的成本必须谨慎处理。</p>
<ul>
<li>Singleton</li>
<li>FlyWeight(享元模式)</li>
</ul>
<h3 id="Singleton（单件模式）"><a href="#Singleton（单件模式）" class="headerlink" title="Singleton（单件模式）"></a>Singleton（单件模式）</h3><p>动机：</p>
<ul>
<li>在软件系统中，经常有这样一些特殊的类，必须保证它们在系统中只存在一个实例，才能确保它们的逻辑正确性、以及良好的效率。</li>
<li>如何绕过常规的构造器，提供一个机制来保证一个类只有一个实例？</li>
<li>这应该是类设计者的责任，而不是使用者的责任。</li>
</ul>
<p>定义：</p>
<p>保证一类仅有一个实例，并提供一个该实例的全局访问点。</p>
<p>总结：</p>
<ul>
<li>Singleton模式中的实例构造器可以设置为protected以允许类派生。</li>
<li>Singleton模式一般不要支持拷贝构造函数和Clone接口，因为这有可能导致多个对象实例，与Singleton模式的初衷违背。</li>
<li>如何实现多线程环境下安全的Singleton？注意对双检查锁的正确实现。</li>
</ul>
<h3 id="FlyWeight-享元模式"><a href="#FlyWeight-享元模式" class="headerlink" title="FlyWeight(享元模式)"></a>FlyWeight(享元模式)</h3><p>动机：</p>
<ul>
<li>在软件系统采用纯粹对象方法的问题处在于大量细粒度的对象会很快充斥在系统中，从而带来很高的运行时代价——主要指内存需求方面的代价。</li>
<li>如何避免大量细粒度对象问题的同时，让外部客户程序仍然能够透明地使用面向对象的方式来进行操作？</li>
</ul>
<p>模式定义：</p>
<p>运用共享技术有效地支持大量细粒度的对象。</p>
<p><img src="https://i.imgur.com/evwgHu7.png" alt=""></p>
<p>总结：</p>
<ul>
<li>面向对象很好地解决了抽象性的问题，但是作为一个运行在机器中的程序实体，我们需要考虑对象的代价问题。Flyweight主要解决面向对象的代价问题，一般不触及面向对象的抽象性问题。</li>
<li>Flyweight采用对象共享的做法来降低系统中对象的个数，从而降低细粒度对象给系统带来的内存压力。在具体实现方面，要注意对象状态的处理。</li>
<li>对象的数量太大从而导致对象内存开销加大——什么样的数量才算大？这需要我们仔细的根据具体应用情况进行评估，而不能凭空臆断。</li>
</ul>
<h2 id="接口隔离模式"><a href="#接口隔离模式" class="headerlink" title="接口隔离模式"></a>接口隔离模式</h2><p>在组件构建过程中，某些接口之间直接的依赖常常会带来很多问题、甚至根本无法实现。采用添加一层间接（稳定）接口，来隔离本来相互紧密关联的接口是一种常见的解决方案。</p>
<p>典型模式</p>
<ul>
<li>Facade</li>
<li>Proxy</li>
<li>Adapter</li>
<li>Mediator</li>
</ul>
<h3 id="Faceade-门面-界面-模式"><a href="#Faceade-门面-界面-模式" class="headerlink" title="Faceade(门面(界面)模式)"></a>Faceade(门面(界面)模式)</h3><p>系统间耦合的复杂度</p>
<p><img src="https://i.imgur.com/GKdJzAC.png" alt=""></p>
<p>动机：</p>
<ul>
<li>上述A方案的问题在于组件的客户和组件中各种复杂的子系统有了过多的耦合，随着外部客户程序和各子系统的演化，这种过多的耦合面临着很多变化的挑战。</li>
<li>如何简化外部客户程序和系统间的交互接口？如何将外部客户程序的演化和内部子系统的变化之间的依赖相互解耦？</li>
</ul>
<p>定义：</p>
<p>为子系统中的一组接口提供一个一致（稳定）的接口，Facade模式定义了一个高层接口，这个接口使得这一个子系统更佳容易使用（复用）。</p>
<p>总结：</p>
<ul>
<li>从客户程序的角度来看，Facade模式简化了整个组件系统的接口，对于组件内部与外部客户程序来说，达到了一种“解耦”的效果——内部子系统的任何不会影响到Facade接口的变化。</li>
<li>Facade设计模式更注重从架构的层次去看整个系统，而不是单个类的层次。Facade很多时候更是一种架构设计模式。</li>
<li>Facade设计模式并非一个集装箱，可以任意地放进任何多个对象。Facade模式中组件的内部应该是“相互耦合关系比较大的一系列组件”，而不是一个简单的功能集合。</li>
</ul>
<p>该模式没有一种特定的代码结构，所以没有代码分析。</p>
<h3 id="Proxy-代理模式"><a href="#Proxy-代理模式" class="headerlink" title="Proxy(代理模式)"></a>Proxy(代理模式)</h3><p>动机：</p>
<ul>
<li>在面向对象系统中，有些对象由于某种原因（比如对象创建的开销很大，或者某些操作需要安全控制，或者需要进程外的访问等），直接访问会给使用者、或者系统结构带来很多麻烦。</li>
<li>如何在不失去透明操作对象的同时来管理/控制这些对象特有的复杂性？增加一层间接层是软件开发中常见的解决方式。</li>
</ul>
<p>定义：</p>
<p>为其他对象提供一种代理以控制（隔离，使用接口）对这个对象的访问。</p>
<p><img src="https://i.imgur.com/sqheZSF.png" alt=""></p>
<p>总结：</p>
<ul>
<li>“增加一层间接层”是软件系统中对许多复杂问题的一种常见解决方法。在面向对象系统中，直接使用某些对象会带来很多问题，作为间接层的proxy对象便是解决这一问题的常用手段。</li>
<li>具体proxy设计模式的实现方法、实现粒度都相差很大，有些可能对单个对象做细粒度的控制，如copy-on-write技术，有些可能对组件模块提供抽象代理层，在架构层次对对象做proxy。</li>
<li>Proxy并不一定要保持接口完成的一致性，只要能够实现间接控制，有时候损失一些透明性是可以接受的。</li>
</ul>
<h3 id="Adapter-适配器"><a href="#Adapter-适配器" class="headerlink" title="Adapter(适配器)"></a>Adapter(适配器)</h3><p>动机：</p>
<ul>
<li>在软件系统中，由于应用环境的变化，常常需要将“一些现存的对象”放在新的环境中应用，但是新环境要求的接口是这些现存对象所不满足的。</li>
<li>如何应对这种“迁移的变化”？如何既能利用现有对象的良好实现，同时又能满足新的应用环境所要求的接口？</li>
</ul>
<p>定义：</p>
<p>将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。</p>
<p><img src="https://i.imgur.com/HUkUxAR.png" alt=""></p>
<p>Adapter实现了从Adptee到Target的转换。</p>
<p>总结：</p>
<ul>
<li>Adpater模式主要应用于“希望复用一些现存的类，但是接口又与复用的环境要求不一致的情况”，在遗留代码复用、类库迁移等方面非常有用。</li>
<li>GoF 23定义了两种Adapter模式的实现结构：对象适配器和类适配器。但类适配器采用“多继承”的实现方式，一般不推荐使用。对象适配器采用“对象组合”的方式，更符合松耦精神。</li>
<li>Adapter模式可以实现的非常灵活，不必拘泥于Gof23中定义的两种接口。例如，完全可以将Adapter模式中的e“现存对象”作为新的接口方法参数，来达到适配的目的。</li>
</ul>
<h3 id="中介者-Mediator"><a href="#中介者-Mediator" class="headerlink" title="中介者(Mediator)"></a>中介者(Mediator)</h3><p>动机：</p>
<ul>
<li>在软件构建过程中，经常会出现多个对象相互关联交互的情况，对象之间常常会维持一种复杂的引用关系，如果遇到一些需求的更改，这种直接的引用关系将面临不断的变化。</li>
<li>在这种情况下，我们可使用一种“中介对象”来管理对象间的关联关系，避免相互交互的对象之间的紧耦合引用关系，从而更好地抵御变化。</li>
</ul>
<p>定义：</p>
<p>用一个中介对象来封装（封装变化）一系列的对象交互。中介者使各对象不需要显式的相互引用（编译时依赖→运行时依赖），从而使其耦合松散（管理变化），而且可以独立地改变它们之间的交互。</p>
<p><img src="https://i.imgur.com/FcFPMtf.png" alt=""></p>
<p>更加形象的结构图：</p>
<p><img src="https://i.imgur.com/4V8TIZo.png" alt=""></p>
<p>总结：</p>
<ul>
<li>将多个对象间复杂的关联关系解耦，Mediator模式间多个对象间的控制逻辑进行集中管理，变“多个对象互相关联”为“多个对象和一个中介者关联”，简化了系统的维护，抵御了可能的变化。</li>
<li>随着控制逻辑的复杂化，Mediator具体对象的实现可能相当复杂。这时候可以对Mediator对象进行分解处理。</li>
<li>Facade模式是解耦系统间（单向）的对象关联关系；Mediator模式使解耦系统内各个对象之间（双向）的关联关系。</li>
</ul>
<h2 id="状态变化模式"><a href="#状态变化模式" class="headerlink" title="状态变化模式"></a>状态变化模式</h2><p>在组件构建过程中，某些对象的状态经常面临变化，如何对这些变化进行有效的管理？同时又维持高层模块的稳定？“状态变化”模式为这一问题提供了一种解决方案。</p>
<p>典型模式：</p>
<ul>
<li>State</li>
<li>Memento</li>
</ul>
<h3 id="State-状态模式"><a href="#State-状态模式" class="headerlink" title="State(状态模式)"></a>State(状态模式)</h3><p>动机：</p>
<ul>
<li>在软件构建过程中，某些对象的状态如果变化，其行为也会随之发生变化，比如文档处于只读状态，其支持的行为和读写状态支持的行为就可以完全不同。</li>
<li>如果在运行时根据对象的状态来透明地更改对象的行为？而不会为对象操作和状态转化之间引入紧耦合？</li>
</ul>
<p>定义：</p>
<p>允许一个对象在其内部状态改变它的行为。从而使对象看起来似乎修改了其行为。</p>
<p><img src="https://i.imgur.com/w8juT8b.png" alt=""></p>
<p>总结：</p>
<ul>
<li>State模式将所有与一个特定状态相关的行为都放入一个State的子类对象中，在对象状态切换时，切换相应的对象；但同时维持State的接口，这样实现了具体操作与状态切换之间的解耦。</li>
<li>为不同的状态引入的对象使得状态转换变得更加明确，而且可以保证不会出现状态不一致的情况，因为转换是原子性的——即要么彻底转换过来，要么不转换。</li>
<li>如果State对象没有实例变量，那么各个上下文可以共享同一State对象，从而节省对象开销。</li>
</ul>
<h3 id="Memento-备忘录模式"><a href="#Memento-备忘录模式" class="headerlink" title="Memento(备忘录模式)"></a>Memento(备忘录模式)</h3><p>动机：</p>
<ul>
<li>在软件构建过程中，某些对象的状态在转换过程中，可能由于某种需要，要求程序能够回溯到对象之前处于某个点时的状态。如果使用一些公有接口来让其对象得到对象的状态，便会暴露对象的细节实现。</li>
<li>如何实现对象状态的良好保存与恢复？但同时又不会因此而破坏对象本身的封装性。</li>
</ul>
<p>定义：</p>
<p>在不破化封装性的前提下，捕获一个对象的内部状态，并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。</p>
<p><img src="https://i.imgur.com/mau83sl.png" alt=""></p>
<p>总结：</p>
<ul>
<li>备忘录（Memento）存储原发器（Originator）对象的内部状态，在需要时恢复原发器状态。</li>
<li>Memento模式的核心是信息隐藏，即Originator需要向外隐藏信息，保持其封装性。但同时又需要将状态保存到外界（Memento）。</li>
<li>由于现代语言运行时（如C#,java等）都具有相当的对象序列化支持，因此往往采用效率较高、又较容易正确实现的序列化方法来实现Memento模式。</li>
</ul>
<h2 id="数据结构模式"><a href="#数据结构模式" class="headerlink" title="数据结构模式"></a>数据结构模式</h2><p>常常有一些组件在内部具有特定的数据结构，如果让客户程序依赖这些特定的数据结构，将极大地破坏组件的复用。这时候，将这些特定数据结构封装在内部，在外部提供统一的接口，来实现与特定数据结构无关的访问，是一种行之有效的解决方案。</p>
<p>典型模式：</p>
<ul>
<li>Composite</li>
<li>Iterator</li>
<li>Chain of Resposibility </li>
</ul>
<h3 id="Composite-组合模式"><a href="#Composite-组合模式" class="headerlink" title="Composite(组合模式)"></a>Composite(组合模式)</h3><p>动机：</p>
<ul>
<li>在软件某些情况下，客户代码过多地依赖于对象容器复杂的内部实现结构，对象容器内部实现结构（而非抽象接口）的变化将引起客户代码的频繁变化，带来了代码的维护性、扩展性等弊端。</li>
<li>如何将“客户代码与复杂的对象容器结构”解耦？让对象容器自己来实现自身的复杂结构，从而使得客户代码就像处理简单对象一样来处理复杂的对象容器？</li>
</ul>
<p>定义：</p>
<p>将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性（稳定）。</p>
<p><img src="https://i.imgur.com/5ndRxkW.png" alt=""></p>
<p>总结：</p>
<ul>
<li>Composite模式采用树形结构来实现普遍存在的对象容器，从而将“一对多”的关系转化为“一对一”的关系，使得客户代码可以一致地（复用）处理对象和对象容器，无需关系处理的是单个的对象，还是组合的对象容器。</li>
<li>将“客户代码与复杂的对象容器结构”解耦是Composite的核心思想，解耦之后，客户代码将与纯粹的抽象接口——而非对象容器的内部实现结构——发生依赖，从而更能“应对变化”。</li>
<li>Composite模式在具体实现中，可以让父对象中的子对象方向追随；如果父对象有频繁的遍历需求，可使用缓存技巧来改善效率。</li>
</ul>
<h3 id="Iterator-迭代器模式"><a href="#Iterator-迭代器模式" class="headerlink" title="Iterator(迭代器模式)"></a>Iterator(迭代器模式)</h3><p>动机：</p>
<ul>
<li>在软件构建过程中，集合对象内部结构常常变化各异。但对于这些集合对象，我们希望在不暴露其内部结构的同时，可以让外部客户代码透明地访问其中包含的元素；同时这种“透明遍历”也为“同一种算法在多种集合对象上进行操作”提供了可能。</li>
<li>使用面向对象技术将这种遍历机制抽象为“迭代器对象”为“应对变化中的集合对象”提供了一种优雅的方式。</li>
</ul>
<p>定义：</p>
<p>提供一种方法顺序访问一个聚合对象中的各个元素，而又不暴露（稳定）该对象的内部表示。</p>
<p><img src="https://i.imgur.com/T8WmrEK.png" alt=""></p>
<p>注意：这种模式已经过时，一般采用泛型编程的迭代器。</p>
<p>总结：</p>
<ul>
<li>迭代抽象：访问一个聚合对象的内线而无需暴露它的内部表示。</li>
<li>迭代多态：为遍历不同的集合结构提供一个统一的接口，从而支持同样的算法在不同的集合结构上进行操作。</li>
<li>迭代器的健壮性考虑：遍历的同时更改迭代器所在的集合结构会导致问题。</li>
</ul>
<h3 id="Chain-of-Resposibility-职责链模式"><a href="#Chain-of-Resposibility-职责链模式" class="headerlink" title="Chain of Resposibility(职责链模式)"></a>Chain of Resposibility(职责链模式)</h3><p>动机：</p>
<ul>
<li>在软件构建过程中，一个请求可能被多个对象处理，但是每个请求在运行时只能有一个接收者，如果显式指定，将必不可少地代理请求发送与接收者的紧耦合。</li>
<li>如何使请求的发送者不需要指定具体的接收者？让请求的接收者自己在运行时决定来处理请求，从而使两者解耦。</li>
</ul>
<p>定义：</p>
<p>使多个对象都有机会处理请求，从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链，并沿着这条链传递请求，直到有一个对象处理它为止。</p>
<p><img src="https://i.imgur.com/yJj68Gh.png" alt=""></p>
<p>总结：</p>
<ul>
<li>Chain of Resposibility模式的应用场合在于“一个请求可能有多个接收者，但是最后真正的接收者只有一个”，这时候请求发送者与接收者的耦合有可能出现“变化脆弱”的症状，职责链的目的就是将二者解耦，从而更好地应对变化。</li>
<li>应用了Chain of Resposibility模式后，对象的职责分派将更具有灵活性。我们可以在运行时动态添加/修改请求的处理职责。</li>
<li>如果请求传递到职责链的末尾仍得不到处理，应该有一个合理缺省机制。这也是每一个接收对象得责任，而不是发出请求得对象得责任。</li>
</ul>
<p>注意：该模式也已经过时。</p>
<h2 id="行为变化模式"><a href="#行为变化模式" class="headerlink" title="行为变化模式"></a>行为变化模式</h2><p>在组件的构建过程中，组件行为的变化经常导致组件本身剧烈的变化。“行为变化”模式将组件的行为和组件本身进行金瓯，从而支持组件行为的变化，实现两者之间的松耦合。</p>
<p>典型模式：</p>
<ul>
<li>Command</li>
<li>Vistor</li>
</ul>
<h3 id="Command-命令模式"><a href="#Command-命令模式" class="headerlink" title="Command(命令模式)"></a>Command(命令模式)</h3><p>动机：</p>
<ul>
<li>在软件构建过程中，“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合——比如需要对行为进行“记录、撤销/重（undo/redo）、事务”等处理，这种无法抵御变化的紧耦合是不适用的。</li>
<li>在这种情况下，如何将“行为请求者”与“行为是想着”解耦？将一组行为抽象为对象，可以实现两者之间的松耦合。】</li>
</ul>
<p>定义：</p>
<p>将一个请求（行为）封装为一个对象，从而使你可用不同的请求对客户进行参数化；对请求排队或记录请求日志，以及持支可撤销的操作。</p>
<p><img src="https://i.imgur.com/nMQ6Qzm.png" alt=""></p>
<p>总结：</p>
<ul>
<li>Command模式的根本目的在于将“行为请求者”与“行为实现者”解耦，在面向对象语言中，常见的实现手段是“将行为抽象为对象”。</li>
<li>实现Command接口的具体命令对象ConcreteCommand有时候根据需要可能会保存一些额外的状态信息。通过使用Composite模式，可以将多个“命令”封装为一个“符合命令”MacroCommand。</li>
<li>Command模式与C++中的函数对象有些类似。但两者定义行为接口的规范有所区别：Command以面向对象中的“接口-实现”来定义行为接口规矩，更严格，但有性能损失；C++函数对象以函数签名来定义行为接口规范，更灵活，性能更高。</li>
</ul>
<h3 id="Vistor-访问器模式"><a href="#Vistor-访问器模式" class="headerlink" title="Vistor(访问器模式)"></a>Vistor(访问器模式)</h3><p>动机：</p>
<ul>
<li>在软件构建过程中，由于需求的变化，某些类层次结构中常常需要增加新的方法，如果直接在基类中做这样的更改，将会给子类带来很繁重的变更负担，甚至破坏原有设计。</li>
<li>如何在不更改类层次结构的前提下，在运行时根据需要透明地为类层次结构上的各个类动态添加新的操作，从而避免上述问题？</li>
</ul>
<p>定义：</p>
<p>表示一个作用于某对象结构中的各元素的操作。使得可以在不改变（稳定）各元素的类的前提下定义（扩展）作用于这些元素的新操作（变化）。</p>
<p><img src="https://i.imgur.com/okHsclo.png" alt=""></p>
<p>注意：Element的子类的数量一定不能变，而且子类不能更改。</p>
<p>总结：</p>
<ul>
<li>Visitor模式通过所谓双重分发（double dispatch）来实现不更改（不添加新的操作-编译时）Element类层次结构的前提下，在运行时透明地为类层次结构的各个类动态添加新的操作（支持变化）。</li>
<li>所谓双重分发即Visitor模式中间包括了两个多态分发（注意其中的多态机制）：第一个为accept方法的多态辨析；第二个为visitElementX方法的多态辨析。</li>
<li>Visitor模式的最大缺点在于扩展类层次结构，会导致Visitor类的改变。因此Visitor模式适用于“Element类层次结构稳定，而其中的操作却经常面临频繁改动”。</li>
</ul>
<h2 id="领域规则模式"><a href="#领域规则模式" class="headerlink" title="领域规则模式"></a>领域规则模式</h2><p>在特定领域，某些变化虽然频繁，但可以抽象为某种规则。这时候，结合特定领域，将问题抽象为语法规则，从而给出在该领域下的一般性解决方案。</p>
<p>典型模式： Interpreter</p>
<h3 id="Interpreter-解析器模式"><a href="#Interpreter-解析器模式" class="headerlink" title="Interpreter(解析器模式)"></a>Interpreter(解析器模式)</h3><p>动机：</p>
<ul>
<li>在软件构建过程中，如果某一特定领域的问题比较复杂，类似的结构不断重复出现，如果使用普通的编程方式来实现面临非常频繁的变化。</li>
<li>在这种情况下，将特定领域的问题表达为某种语法规则下的句子，然后构建一个解释器来解释这样的句子，从而达到解决问题的目的。</li>
</ul>
<p>代码中对应的计算表达式：</p>
<p><img src="https://i.imgur.com/trfzu5c.png" alt=""></p>
<p>定义：</p>
<p>给定一种语言，定义它的文法的一种表示，并顶一个一种解释器，这个解释器使用该表示来解释语言中的句子。</p>
<p><img src="https://i.imgur.com/pRWHiGV.png" alt=""></p>
<p>TerminalExpression:相当于代码中变量表达式</p>
<p>NonterminalExpression：相当于代码中的符号表达式</p>
<p>总结：</p>
<ul>
<li>Interpreter模式的应用场合是Interpreter模式应用中的难点，只有满足“业务规则频繁变化，且类似的结构不断重复出现，并且容易抽象为语法规则的问题”才适合使用interpreter模式。</li>
<li>使用Interpreter模式来表达文法规则，从而可以使用面向对象技巧来方便地“扩展文法”。</li>
<li>Interpreter模式比较使用简单的文法表示，对于复杂的文法表示，Interperter模式会产生比较大的类层次结构，需要求助于语法分析生成器这样的标准工具。</li>
</ul>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><h3 id="一个目标："><a href="#一个目标：" class="headerlink" title="一个目标："></a>一个目标：</h3><p>管理变化，提供复用！</p>
<h3 id="两个手段："><a href="#两个手段：" class="headerlink" title="两个手段："></a>两个手段：</h3><p>分解 vs 抽象</p>
<h3 id="八大原则："><a href="#八大原则：" class="headerlink" title="八大原则："></a>八大原则：</h3><ul>
<li>依赖倒置</li>
<li>开放封闭原则</li>
<li>单一职责原则</li>
<li>Liskov替换原则</li>
<li>接口隔离原则</li>
<li>对象组合优于类继承</li>
<li>封装变化点</li>
<li>面向接口编程</li>
</ul>
<h3 id="重构技巧："><a href="#重构技巧：" class="headerlink" title="重构技巧："></a>重构技巧：</h3><p>静态 -&gt; 动态</p>
<p>早绑定 -&gt; 晚绑定</p>
<p>继承 -&gt; 组合</p>
<p>编译时依赖 -&gt; 运行时依赖</p>
<p>紧耦合 -&gt; 松耦合</p>
<h3 id="模式分类"><a href="#模式分类" class="headerlink" title="模式分类"></a>模式分类</h3><p><img src="https://i.imgur.com/6pdgMH0.png" alt=""></p>
<p>不常用的： Builder、Mediator、Memento、Iterator、Chain of REsposibility、Command、Interpreter</p>
<p>C++对象模型：</p>
<p><img src="https://i.imgur.com/ucRfqWn.png" alt=""></p>
<p>第三种对象模型中，模式都是通过一个指针来指向一个多态对象。</p>
<p>什么时候不用模式：</p>
<ul>
<li>代码可读性很差时</li>
<li>需求理解还很浅时</li>
<li>变化没有显现时</li>
<li>不是系统的关键依赖点</li>
<li>项目没有复用价值时</li>
<li>项目将要发布时</li>
</ul>
<p>经验之谈：</p>
<ul>
<li>不要为模式而模式</li>
<li>关注抽象类&amp;接口</li>
<li>理清变化点和稳定点</li>
<li>审视依赖关系</li>
<li>要有Framework和Application的区隔思维</li>
<li>良好的设计是演化的结果</li>
</ul>
<p>设计模式成长之路：</p>
<ul>
<li>“手中无剑，心中无剑”：见模式而不知</li>
<li>“手中有剑，心中无剑”：可以识别模式，作为应用开发人员使用模式</li>
<li>“手中有剑，心中有剑”：作为框架开发人员为应用设计某些模式</li>
<li>“手中无剑，心中有剑”：忘掉模式，只有原则</li>
</ul>
<p>资料：<a href="https://blog.csdn.net/liang19890820/article/category/6783147" target="_blank" rel="noopener">https://blog.csdn.net/liang19890820/article/category/6783147</a></p>

      
    </div>
    
    
    

    
      <div>
        <div id="wechat_subscriber" style="display: block; padding: 10px 0; margin: 20px auto; width: 100%; text-align: center">
    <img id="wechat_subscriber_qcode" src="/images/wechat-qcode.jpg" alt="zxp wechat" style="width: 200px; max-width: 100%;"/>
    <div>欢迎关注微信公众号！</div>
</div>

      </div>
    

    
      <div>
        <div style="padding: 10px 0; margin: 20px auto; width: 90%; text-align: center;">
  <div></div>
  <button id="rewardButton" disable="enable" onclick="var qr = document.getElementById('QR'); if (qr.style.display === 'none') {qr.style.display='block';} else {qr.style.display='none'}">
    <span>打赏</span>
  </button>
  <div id="QR" style="display: none;">

    
      <div id="wechat" style="display: inline-block">
        <img id="wechat_qr" src="/images/WeChatpay.jpg" alt="zxp 微信支付"/>
        <p>微信支付</p>
      </div>
    

    
      <div id="alipay" style="display: inline-block">
        <img id="alipay_qr" src="/images/Alipay.jpg" alt="zxp 支付宝"/>
        <p>支付宝</p>
      </div>
    

    

  </div>
</div>

      </div>
    

    
      <div>
        <ul class="post-copyright">
  <li class="post-copyright-author">
    <strong>本文作者：</strong>
    zxp
  </li>
  <li class="post-copyright-link">
    <strong>本文链接：</strong>
    <a href="https://zxpgo.github.io/2019/08/02/设计模式/" title="C++版设计模式">https://zxpgo.github.io/2019/08/02/设计模式/</a>
  </li>
  <li class="post-copyright-license">
    <strong>版权声明： </strong>
    本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/3.0/" rel="external nofollow" target="_blank">CC BY-NC-SA 3.0</a> 许可协议。转载请注明出处！
  </li>
</ul>

      </div>
    

    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/tag/C/" rel="tag"># C++</a>
          
        </div>
      

      
      
        <div class="post-widgets">
        

        

        
          
          <div id="needsharebutton-postbottom">
            <span class="btn">
              <i class="fa fa-share-alt" aria-hidden="true"></i>
            </span>
          </div>
        
        </div>
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/2019/07/08/QT/" rel="next" title="QT教程">
                <i class="fa fa-chevron-left"></i> QT教程
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/2019/08/03/C++2.0新特性/" rel="prev" title="C++2.0(11)新特性">
                C++2.0(11)新特性 <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          


          

  
    <div class="comments" id="comments">
      <div id="lv-container" data-id="city" data-uid="MTAyMC8zODgxNC8xNTM0Mg=="></div>
    </div>

  



        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <img class="site-author-image" itemprop="image"
                src="/images/avatar.gif"
                alt="zxp" />
            
              <p class="site-author-name" itemprop="name">zxp</p>
              <p class="site-description motion-element" itemprop="description"></p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/archives/">
              
                  <span class="site-state-item-count">176</span>
                  <span class="site-state-item-name">日志</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-categories">
                <a href="/categories/index.html">
                  <span class="site-state-item-count">16</span>
                  <span class="site-state-item-name">分类</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-tags">
                <a href="/tags/index.html">
                  <span class="site-state-item-count">48</span>
                  <span class="site-state-item-name">标签</span>
                </a>
              </div>
            

          </nav>

          
            <div class="feed-link motion-element">
              <a href="/atom.xml" rel="alternate">
                <i class="fa fa-rss"></i>
                RSS
              </a>
            </div>
          

          
            <div class="links-of-author motion-element">
                
                  <span class="links-of-author-item">
                    <a href="https://blog.csdn.net/qq_25774883" target="_blank" title="CSDN">
                      
                        <i class="fa fa-fw fa-globe"></i>CSDN</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="https://github.com/zxpgo/zxpgo" target="_blank" title="GitHub">
                      
                        <i class="fa fa-fw fa-globe"></i>GitHub</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="https://www.linkedin.com/feed/" target="_blank" title="LinkedIn">
                      
                        <i class="fa fa-fw fa-globe"></i>LinkedIn</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="1165772354@qq.com" target="_blank" title="E-Mail">
                      
                        <i class="fa fa-fw fa-envelope"></i>E-Mail</a>
                  </span>
                
            </div>
          

          
          

          
          
            <div class="links-of-blogroll motion-element links-of-blogroll-inline">
              <div class="links-of-blogroll-title">
                <i class="fa  fa-fw fa-link"></i>
                友情链接
              </div>
              <ul class="links-of-blogroll-list">
                
                  <li class="links-of-blogroll-item">
                    <a href="http://theme-next.iissnan.com" title="Next主题" target="_blank">Next主题</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="http://blog.rexking6.top" title="青爷博客" target="_blank">青爷博客</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://me.csdn.net/download/qq_25774883" title="CSDN下载" target="_blank">CSDN下载</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://www.livere.com/" title="来必力" target="_blank">来必力</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://tongji.baidu.com/web/welcome/login" title="百度统计" target="_blank">百度统计</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://leancloud.cn/" title="LeanCloud" target="_blank">LeanCloud</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="http://ibruce.info/2015/04/04/busuanzi/" title="不蒜子" target="_blank">不蒜子</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://leetcode-cn.com/" title="LeetCode" target="_blank">LeetCode</a>
                  </li>
                
              </ul>
            </div>
          

          

        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#引言"><span class="nav-number">1.</span> <span class="nav-text">引言</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#设计原则"><span class="nav-number">2.</span> <span class="nav-text">设计原则</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#重新认识面向对象"><span class="nav-number">2.1.</span> <span class="nav-text">重新认识面向对象</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#面向对象设计原则"><span class="nav-number">2.2.</span> <span class="nav-text">面向对象设计原则</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#1-依赖导致原则（DIP）"><span class="nav-number">2.2.1.</span> <span class="nav-text">1.依赖导致原则（DIP）</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#2-开放封闭原则-OCP"><span class="nav-number">2.2.2.</span> <span class="nav-text">2.开放封闭原则(OCP)</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#3-单一职责原则-SRP"><span class="nav-number">2.2.3.</span> <span class="nav-text">3.单一职责原则(SRP)</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#4-LisKov替换原则-LSP"><span class="nav-number">2.2.4.</span> <span class="nav-text">4.LisKov替换原则(LSP)</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#5-接口隔离原则-ISP"><span class="nav-number">2.2.5.</span> <span class="nav-text">5.接口隔离原则(ISP)</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#6-优先使用对象组合，而不是类继承"><span class="nav-number">2.2.6.</span> <span class="nav-text">6.优先使用对象组合，而不是类继承</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#7-封装变化点"><span class="nav-number">2.2.7.</span> <span class="nav-text">7.封装变化点</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#8-针对接口编程，而不是针对实现编程"><span class="nav-number">2.2.8.</span> <span class="nav-text">8.针对接口编程，而不是针对实现编程</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#设计原则提升为设计经验"><span class="nav-number">2.3.</span> <span class="nav-text">设计原则提升为设计经验</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#设计模式"><span class="nav-number">3.</span> <span class="nav-text">设计模式</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#组件协作模式"><span class="nav-number">4.</span> <span class="nav-text">组件协作模式</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Tempalte-Method-模板方法"><span class="nav-number">4.1.</span> <span class="nav-text">Tempalte Method(模板方法)</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#动机"><span class="nav-number">4.1.1.</span> <span class="nav-text">动机</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#定义"><span class="nav-number">4.1.2.</span> <span class="nav-text">定义</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Strategy-策略模式"><span class="nav-number">4.2.</span> <span class="nav-text">Strategy(策略模式)</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#动机-1"><span class="nav-number">4.2.1.</span> <span class="nav-text">动机</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#定义-1"><span class="nav-number">4.2.2.</span> <span class="nav-text">定义</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Observer-观察者模式"><span class="nav-number">4.3.</span> <span class="nav-text">Observer(观察者模式)</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#动机-2"><span class="nav-number">4.3.1.</span> <span class="nav-text">动机</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#定义-2"><span class="nav-number">4.3.2.</span> <span class="nav-text">定义</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#单一职责模式"><span class="nav-number">5.</span> <span class="nav-text">单一职责模式</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Decorate-装饰模式"><span class="nav-number">5.1.</span> <span class="nav-text">Decorate(装饰模式)</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#动机-3"><span class="nav-number">5.1.1.</span> <span class="nav-text">动机</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#定义-3"><span class="nav-number">5.1.2.</span> <span class="nav-text">定义</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Bridge-桥模式"><span class="nav-number">5.2.</span> <span class="nav-text">Bridge(桥模式)</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#动机-4"><span class="nav-number">5.2.1.</span> <span class="nav-text">动机</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#定义-4"><span class="nav-number">5.2.2.</span> <span class="nav-text">定义</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#对象创建模式"><span class="nav-number">6.</span> <span class="nav-text">对象创建模式</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Factory-Method（工厂方法模式）"><span class="nav-number">6.1.</span> <span class="nav-text">Factory Method（工厂方法模式）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Abstract-Factory抽象工厂"><span class="nav-number">6.2.</span> <span class="nav-text">Abstract Factory抽象工厂</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Prototype（原型模式）"><span class="nav-number">6.3.</span> <span class="nav-text">Prototype（原型模式）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Builder（构建器）"><span class="nav-number">6.4.</span> <span class="nav-text">Builder（构建器）</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#对象性能模式"><span class="nav-number">7.</span> <span class="nav-text">对象性能模式</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Singleton（单件模式）"><span class="nav-number">7.1.</span> <span class="nav-text">Singleton（单件模式）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#FlyWeight-享元模式"><span class="nav-number">7.2.</span> <span class="nav-text">FlyWeight(享元模式)</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#接口隔离模式"><span class="nav-number">8.</span> <span class="nav-text">接口隔离模式</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Faceade-门面-界面-模式"><span class="nav-number">8.1.</span> <span class="nav-text">Faceade(门面(界面)模式)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Proxy-代理模式"><span class="nav-number">8.2.</span> <span class="nav-text">Proxy(代理模式)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Adapter-适配器"><span class="nav-number">8.3.</span> <span class="nav-text">Adapter(适配器)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#中介者-Mediator"><span class="nav-number">8.4.</span> <span class="nav-text">中介者(Mediator)</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#状态变化模式"><span class="nav-number">9.</span> <span class="nav-text">状态变化模式</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#State-状态模式"><span class="nav-number">9.1.</span> <span class="nav-text">State(状态模式)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Memento-备忘录模式"><span class="nav-number">9.2.</span> <span class="nav-text">Memento(备忘录模式)</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#数据结构模式"><span class="nav-number">10.</span> <span class="nav-text">数据结构模式</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Composite-组合模式"><span class="nav-number">10.1.</span> <span class="nav-text">Composite(组合模式)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Iterator-迭代器模式"><span class="nav-number">10.2.</span> <span class="nav-text">Iterator(迭代器模式)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Chain-of-Resposibility-职责链模式"><span class="nav-number">10.3.</span> <span class="nav-text">Chain of Resposibility(职责链模式)</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#行为变化模式"><span class="nav-number">11.</span> <span class="nav-text">行为变化模式</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Command-命令模式"><span class="nav-number">11.1.</span> <span class="nav-text">Command(命令模式)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Vistor-访问器模式"><span class="nav-number">11.2.</span> <span class="nav-text">Vistor(访问器模式)</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#领域规则模式"><span class="nav-number">12.</span> <span class="nav-text">领域规则模式</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Interpreter-解析器模式"><span class="nav-number">12.1.</span> <span class="nav-text">Interpreter(解析器模式)</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#总结"><span class="nav-number">13.</span> <span class="nav-text">总结</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#一个目标："><span class="nav-number">13.1.</span> <span class="nav-text">一个目标：</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#两个手段："><span class="nav-number">13.2.</span> <span class="nav-text">两个手段：</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#八大原则："><span class="nav-number">13.3.</span> <span class="nav-text">八大原则：</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#重构技巧："><span class="nav-number">13.4.</span> <span class="nav-text">重构技巧：</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#模式分类"><span class="nav-number">13.5.</span> <span class="nav-text">模式分类</span></a></li></ol></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div>
<script async src="https//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
<i class="fa fa-user-md"></i><span id="busuanzi_container_site_pv" style='display:none'>
    本站总访问量 <span id="busuanzi_value_site_pv"></span> 
    <span class="post-meta-divider">|</span>
</span>
<span id="busuanzi_container_site_uv" style='display:none'>
    访问人数 <span id="busuanzi_value_site_uv"></span>
</span>
</div>


<script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>

<div class="copyright">&copy; 2018-8 &mdash; <span itemprop="copyrightYear">2020</span>
  <span class="with-love">
    <i class="fa fa-"></i> Power by 
  </span>
  <span class="author" itemprop="copyrightHolder">zxp</span>
  
  
</div>









        







        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    
      <div id="needsharebutton-float">
        <span class="btn">
          <i class="fa fa-share-alt" aria-hidden="true"></i>
        </span>
      </div>
    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  












  
  
    <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  


  


  <script type="text/javascript" src="/js/src/utils.js?v=5.1.4"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=5.1.4"></script>



  
  


  <script type="text/javascript" src="/js/src/affix.js?v=5.1.4"></script>

  <script type="text/javascript" src="/js/src/schemes/pisces.js?v=5.1.4"></script>



  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.4"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=5.1.4"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.4"></script>



  


  




	





  




  
  <div id="lv-container" data-uid="MTAyMC8zODgxNC8xNTM0Mg==">
    <script type="text/javascript">
      (function(d, s) {
        var j, e = d.getElementsByTagName(s)[0];
        if (typeof LivereTower === 'function') { return; }
        j = d.createElement(s);
        j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
        j.async = true;
        e.parentNode.insertBefore(j, e);
      })(document, 'script');
    </script>
	</div>
  











  

  <script type="text/javascript">
    // Popup Window;
    var isfetched = false;
    var isXml = true;
    // Search DB path;
    var search_path = "search.xml";
    if (search_path.length === 0) {
      search_path = "search.xml";
    } else if (/json$/i.test(search_path)) {
      isXml = false;
    }
    var path = "/" + search_path;
    // monitor main search box;

    var onPopupClose = function (e) {
      $('.popup').hide();
      $('#local-search-input').val('');
      $('.search-result-list').remove();
      $('#no-result').remove();
      $(".local-search-pop-overlay").remove();
      $('body').css('overflow', '');
    }

    function proceedsearch() {
      $("body")
        .append('<div class="search-popup-overlay local-search-pop-overlay"></div>')
        .css('overflow', 'hidden');
      $('.search-popup-overlay').click(onPopupClose);
      $('.popup').toggle();
      var $localSearchInput = $('#local-search-input');
      $localSearchInput.attr("autocapitalize", "none");
      $localSearchInput.attr("autocorrect", "off");
      $localSearchInput.focus();
    }

    // search function;
    var searchFunc = function(path, search_id, content_id) {
      'use strict';

      // start loading animation
      $("body")
        .append('<div class="search-popup-overlay local-search-pop-overlay">' +
          '<div id="search-loading-icon">' +
          '<i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>' +
          '</div>' +
          '</div>')
        .css('overflow', 'hidden');
      $("#search-loading-icon").css('margin', '20% auto 0 auto').css('text-align', 'center');

      $.ajax({
        url: path,
        dataType: isXml ? "xml" : "json",
        async: true,
        success: function(res) {
          // get the contents from search data
          isfetched = true;
          $('.popup').detach().appendTo('.header-inner');
          var datas = isXml ? $("entry", res).map(function() {
            return {
              title: $("title", this).text(),
              content: $("content",this).text(),
              url: $("url" , this).text()
            };
          }).get() : res;
          var input = document.getElementById(search_id);
          var resultContent = document.getElementById(content_id);
          var inputEventFunction = function() {
            var searchText = input.value.trim().toLowerCase();
            var keywords = searchText.split(/[\s\-]+/);
            if (keywords.length > 1) {
              keywords.push(searchText);
            }
            var resultItems = [];
            if (searchText.length > 0) {
              // perform local searching
              datas.forEach(function(data) {
                var isMatch = false;
                var hitCount = 0;
                var searchTextCount = 0;
                var title = data.title.trim();
                var titleInLowerCase = title.toLowerCase();
                var content = data.content.trim().replace(/<[^>]+>/g,"");
                var contentInLowerCase = content.toLowerCase();
                var articleUrl = decodeURIComponent(data.url);
                var indexOfTitle = [];
                var indexOfContent = [];
                // only match articles with not empty titles
                if(title != '') {
                  keywords.forEach(function(keyword) {
                    function getIndexByWord(word, text, caseSensitive) {
                      var wordLen = word.length;
                      if (wordLen === 0) {
                        return [];
                      }
                      var startPosition = 0, position = [], index = [];
                      if (!caseSensitive) {
                        text = text.toLowerCase();
                        word = word.toLowerCase();
                      }
                      while ((position = text.indexOf(word, startPosition)) > -1) {
                        index.push({position: position, word: word});
                        startPosition = position + wordLen;
                      }
                      return index;
                    }

                    indexOfTitle = indexOfTitle.concat(getIndexByWord(keyword, titleInLowerCase, false));
                    indexOfContent = indexOfContent.concat(getIndexByWord(keyword, contentInLowerCase, false));
                  });
                  if (indexOfTitle.length > 0 || indexOfContent.length > 0) {
                    isMatch = true;
                    hitCount = indexOfTitle.length + indexOfContent.length;
                  }
                }

                // show search results

                if (isMatch) {
                  // sort index by position of keyword

                  [indexOfTitle, indexOfContent].forEach(function (index) {
                    index.sort(function (itemLeft, itemRight) {
                      if (itemRight.position !== itemLeft.position) {
                        return itemRight.position - itemLeft.position;
                      } else {
                        return itemLeft.word.length - itemRight.word.length;
                      }
                    });
                  });

                  // merge hits into slices

                  function mergeIntoSlice(text, start, end, index) {
                    var item = index[index.length - 1];
                    var position = item.position;
                    var word = item.word;
                    var hits = [];
                    var searchTextCountInSlice = 0;
                    while (position + word.length <= end && index.length != 0) {
                      if (word === searchText) {
                        searchTextCountInSlice++;
                      }
                      hits.push({position: position, length: word.length});
                      var wordEnd = position + word.length;

                      // move to next position of hit

                      index.pop();
                      while (index.length != 0) {
                        item = index[index.length - 1];
                        position = item.position;
                        word = item.word;
                        if (wordEnd > position) {
                          index.pop();
                        } else {
                          break;
                        }
                      }
                    }
                    searchTextCount += searchTextCountInSlice;
                    return {
                      hits: hits,
                      start: start,
                      end: end,
                      searchTextCount: searchTextCountInSlice
                    };
                  }

                  var slicesOfTitle = [];
                  if (indexOfTitle.length != 0) {
                    slicesOfTitle.push(mergeIntoSlice(title, 0, title.length, indexOfTitle));
                  }

                  var slicesOfContent = [];
                  while (indexOfContent.length != 0) {
                    var item = indexOfContent[indexOfContent.length - 1];
                    var position = item.position;
                    var word = item.word;
                    // cut out 100 characters
                    var start = position - 20;
                    var end = position + 80;
                    if(start < 0){
                      start = 0;
                    }
                    if (end < position + word.length) {
                      end = position + word.length;
                    }
                    if(end > content.length){
                      end = content.length;
                    }
                    slicesOfContent.push(mergeIntoSlice(content, start, end, indexOfContent));
                  }

                  // sort slices in content by search text's count and hits' count

                  slicesOfContent.sort(function (sliceLeft, sliceRight) {
                    if (sliceLeft.searchTextCount !== sliceRight.searchTextCount) {
                      return sliceRight.searchTextCount - sliceLeft.searchTextCount;
                    } else if (sliceLeft.hits.length !== sliceRight.hits.length) {
                      return sliceRight.hits.length - sliceLeft.hits.length;
                    } else {
                      return sliceLeft.start - sliceRight.start;
                    }
                  });

                  // select top N slices in content

                  var upperBound = parseInt('1');
                  if (upperBound >= 0) {
                    slicesOfContent = slicesOfContent.slice(0, upperBound);
                  }

                  // highlight title and content

                  function highlightKeyword(text, slice) {
                    var result = '';
                    var prevEnd = slice.start;
                    slice.hits.forEach(function (hit) {
                      result += text.substring(prevEnd, hit.position);
                      var end = hit.position + hit.length;
                      result += '<b class="search-keyword">' + text.substring(hit.position, end) + '</b>';
                      prevEnd = end;
                    });
                    result += text.substring(prevEnd, slice.end);
                    return result;
                  }

                  var resultItem = '';

                  if (slicesOfTitle.length != 0) {
                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + highlightKeyword(title, slicesOfTitle[0]) + "</a>";
                  } else {
                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + title + "</a>";
                  }

                  slicesOfContent.forEach(function (slice) {
                    resultItem += "<a href='" + articleUrl + "'>" +
                      "<p class=\"search-result\">" + highlightKeyword(content, slice) +
                      "...</p>" + "</a>";
                  });

                  resultItem += "</li>";
                  resultItems.push({
                    item: resultItem,
                    searchTextCount: searchTextCount,
                    hitCount: hitCount,
                    id: resultItems.length
                  });
                }
              })
            };
            if (keywords.length === 1 && keywords[0] === "") {
              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-search fa-5x" /></div>'
            } else if (resultItems.length === 0) {
              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>'
            } else {
              resultItems.sort(function (resultLeft, resultRight) {
                if (resultLeft.searchTextCount !== resultRight.searchTextCount) {
                  return resultRight.searchTextCount - resultLeft.searchTextCount;
                } else if (resultLeft.hitCount !== resultRight.hitCount) {
                  return resultRight.hitCount - resultLeft.hitCount;
                } else {
                  return resultRight.id - resultLeft.id;
                }
              });
              var searchResultList = '<ul class=\"search-result-list\">';
              resultItems.forEach(function (result) {
                searchResultList += result.item;
              })
              searchResultList += "</ul>";
              resultContent.innerHTML = searchResultList;
            }
          }

          if ('auto' === 'auto') {
            input.addEventListener('input', inputEventFunction);
          } else {
            $('.search-icon').click(inputEventFunction);
            input.addEventListener('keypress', function (event) {
              if (event.keyCode === 13) {
                inputEventFunction();
              }
            });
          }

          // remove loading animation
          $(".local-search-pop-overlay").remove();
          $('body').css('overflow', '');

          proceedsearch();
        }
      });
    }

    // handle and trigger popup window;
    $('.popup-trigger').click(function(e) {
      e.stopPropagation();
      if (isfetched === false) {
        searchFunc(path, 'local-search-input', 'local-search-result');
      } else {
        proceedsearch();
      };
    });

    $('.popup-btn-close').click(onPopupClose);
    $('.popup').click(function(e){
      e.stopPropagation();
    });
    $(document).on('keyup', function (event) {
      var shouldDismissSearchPopup = event.which === 27 &&
        $('.search-popup').is(':visible');
      if (shouldDismissSearchPopup) {
        onPopupClose();
      }
    });
  </script>





  

  
  <script src="https://cdn1.lncld.net/static/js/av-core-mini-0.6.4.js"></script>
  <script>AV.initialize("2AyV3DKioBSdoryrFLRohzjB-gzGzoHsz", "XynedcHyJCVCrTfbD4yYnodo");</script>
  <script>
    function showTime(Counter) {
      var query = new AV.Query(Counter);
      var entries = [];
      var $visitors = $(".leancloud_visitors");

      $visitors.each(function () {
        entries.push( $(this).attr("id").trim() );
      });

      query.containedIn('url', entries);
      query.find()
        .done(function (results) {
          var COUNT_CONTAINER_REF = '.leancloud-visitors-count';

          if (results.length === 0) {
            $visitors.find(COUNT_CONTAINER_REF).text(0);
            return;
          }

          for (var i = 0; i < results.length; i++) {
            var item = results[i];
            var url = item.get('url');
            var time = item.get('time');
            var element = document.getElementById(url);

            $(element).find(COUNT_CONTAINER_REF).text(time);
          }
          for(var i = 0; i < entries.length; i++) {
            var url = entries[i];
            var element = document.getElementById(url);
            var countSpan = $(element).find(COUNT_CONTAINER_REF);
            if( countSpan.text() == '') {
              countSpan.text(0);
            }
          }
        })
        .fail(function (object, error) {
          console.log("Error: " + error.code + " " + error.message);
        });
    }

    function addCount(Counter) {
      var $visitors = $(".leancloud_visitors");
      var url = $visitors.attr('id').trim();
      var title = $visitors.attr('data-flag-title').trim();
      var query = new AV.Query(Counter);

      query.equalTo("url", url);
      query.find({
        success: function(results) {
          if (results.length > 0) {
            var counter = results[0];
            counter.fetchWhenSave(true);
            counter.increment("time");
            counter.save(null, {
              success: function(counter) {
                var $element = $(document.getElementById(url));
                $element.find('.leancloud-visitors-count').text(counter.get('time'));
              },
              error: function(counter, error) {
                console.log('Failed to save Visitor num, with error message: ' + error.message);
              }
            });
          } else {
            var newcounter = new Counter();
            /* Set ACL */
            var acl = new AV.ACL();
            acl.setPublicReadAccess(true);
            acl.setPublicWriteAccess(true);
            newcounter.setACL(acl);
            /* End Set ACL */
            newcounter.set("title", title);
            newcounter.set("url", url);
            newcounter.set("time", 1);
            newcounter.save(null, {
              success: function(newcounter) {
                var $element = $(document.getElementById(url));
                $element.find('.leancloud-visitors-count').text(newcounter.get('time'));
              },
              error: function(newcounter, error) {
                console.log('Failed to create');
              }
            });
          }
        },
        error: function(error) {
          console.log('Error:' + error.code + " " + error.message);
        }
      });
    }

    $(function() {
      var Counter = AV.Object.extend("Counter");
      if ($('.leancloud_visitors').length == 1) {
        addCount(Counter);
      } else if ($('.post-title-link').length > 1) {
        showTime(Counter);
      }
    });
  </script>



  

  
<script>
(function(){
    var bp = document.createElement('script');
    var curProtocol = window.location.protocol.split(':')[0];
    if (curProtocol === 'https') {
        bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';        
    }
    else {
        bp.src = 'http://push.zhanzhang.baidu.com/push.js';
    }
    var s = document.getElementsByTagName("script")[0];
    s.parentNode.insertBefore(bp, s);
})();
</script>


  
  
  
  <link rel="stylesheet" href="/lib/needsharebutton/needsharebutton.css">

  
  
  <script src="/lib/needsharebutton/needsharebutton.js"></script>

  <script>
    
      pbOptions = {};
      
          pbOptions.iconStyle = "default";
      
          pbOptions.boxForm = "vertical";
      
          pbOptions.position = "top";
      
          pbOptions.networks = "Weibo,Wechat,Douban,QQZone,Twitter,Facebook";
      
      new needShareButton('#needsharebutton-postbottom', pbOptions);
    
    
      flOptions = {};
      
          flOptions.iconStyle = "box";
      
          flOptions.boxForm = "horizontal";
      
          flOptions.position = "middleRight";
      
          flOptions.networks = "Weibo,Wechat,Douban,QQZone,Twitter,Facebook";
      
      new needShareButton('#needsharebutton-float', flOptions);
    
  </script>

  

  
  
    <script type="text/x-mathjax-config">
      MathJax.Hub.Config({
        tex2jax: {
          inlineMath: [ ['$','$'], ["\\(","\\)"]  ],
          processEscapes: true,
          skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
        }
      });
    </script>

    <script type="text/x-mathjax-config">
      MathJax.Hub.Queue(function() {
        var all = MathJax.Hub.getAllJax(), i;
        for (i=0; i < all.length; i += 1) {
          all[i].SourceElement().parentNode.className += ' has-jax';
        }
      });
    </script>
    <script type="text/javascript" src="//cdn.bootcss.com/mathjax/2.7.1/latest.js?config=TeX-AMS-MML_HTMLorMML"></script>
  


  
  <script type="text/javascript" src="/js/src/js.cookie.js?v=5.1.4"></script>
  <script type="text/javascript" src="/js/src/scroll-cookie.js?v=5.1.4"></script>


  
  <script type="text/javascript" src="/js/src/exturl.js?v=5.1.4"></script>


  
  
  	 <!-- custom analytics part create by xiamo -->
<script src="https://cdn1.lncld.net/static/js/av-core-mini-0.6.1.js"></script>
<script>AV.initialize("2AyV3DKioBSdoryrFLRohzjB-gzGzoHsz", "XynedcHyJCVCrTfbD4yYnodo");</script>
<script>
function showTime(Counter) {
	var query = new AV.Query(Counter);
	$(".leancloud_visitors").each(function() {
		var url = $(this).attr("id").trim();
		query.equalTo("url", url);
		query.find({
			success: function(results) {
				if (results.length == 0) {
					var content = $(document.getElementById(url)).text() + ' 0';
					$(document.getElementById(url)).text(content);
					return;
				}
				for (var i = 0; i < results.length; i++) {
					var object = results[i];
					var content = $(document.getElementById(url)).text() + ' ' + object.get('time');
					$(document.getElementById(url)).text(content);
				}
			}
		});

	});
}

</script>
  
</body>
</html>
